library(nat)
library(neuprintr)
library(tidyverse)
library(ggraph)
library(plotly)
library(dplyr)
library(pbapply)
library(tidygraph)
library(gridExtra)
library(neuprintrExtra)
library(paletteer)
library(alphahull)

# Go to the neurpintR-notebooks directory before loading these functions
source("visualizeConnectivityTables.R")
# source("neuprintQueryUtils.R")
source("R/table2ggraphUtils.R")
source("R/connectivityMatricesTools.R")
# source("InputOutputByTypeUtils.R")
# source("R/rois.R")
# source("R/supertypeUtils.R")
# source("colorCodeLookup.R")
source("pathways.R")
source("inputOutputRegionsVis.R")
source("GetNeuronsInRoi.R")
source("FBNetworkVisUtils.R")
source("CX_ContextualAnalysisUtils.R")
# source("/Users/danc/repos/neuprintrExtra/R/neuronBag.R")

options(nat.plotengine = 'rgl')

Configurable inputs: choose save/plot directories

# Directory to save data to.
SaveDir = "/Users/danc/Dropbox (HHMI)/WorkDrop/Data/FIBSEM_Analysis"

# Directory to save plots to.
PlotDir = "/Users/danc/Dropbox (HHMI)/WorkDrop/Data/FIBSEM_Analysis"

Create save/plot directory if they dont exists yet

if (!dir.exists(SaveDir)){dir.create(SaveDir)}
if (!dir.exists(PlotDir)){dir.create(PlotDir)}

Connect to neuprint

# neuprint_login()
neuprint_login(config=httr::set_config(httr::config(ssl_verifypeer = 0L)))

Creating a relatively high level ROI set

roisH <- getRoiTree()
roiSelection <- selectRoiSet(roisH,default_level=1,exceptions=list("CX"=2))

Pulling neurons postsynaptic to MBONs

MBON_Names <- neuprint_search("MBON.*")
# Find the postsynaptic partners of all MBONs
MBON_PostConnections <- getConnectionTable(MBON_Names,synapseType = "POST") ## Keeping the default threshold (3)

Get MBON targets inside / outside of CX

# Selecting CX types as those present in Romain's list.
CXTypes <- supertype(read_csv("/Users/danc/Dropbox (HHMI)/WorkDrop/Manuscripts/FIBSEM CX Paper Jan 2020/CX-cell-types060920") %>% rename(databaseType=n.type))
MBON_targets <- unique(MBON_PostConnections$type.to)

# Pick MBON targets in the CX
MBONCXTargetsL <- MBON_targets[MBON_targets %in% CXTypes$databaseType]
MBONCXTargets <- getTypesTable(MBONCXTargetsL)

# Pick MBON targets not in the CX
MBONnonCXTargetsL <- MBON_targets[!(MBON_targets %in% CXTypes$databaseType)]
MBONnonCXTargetsL <- na.omit(MBONnonCXTargetsL)
# Exclude MBONs from this list
MBONnonCXTargetsL <- MBONnonCXTargetsL[!(MBONnonCXTargetsL %in% MBON_Names$type)]
MBONnonCXTargets <- getTypesTable(MBONnonCXTargetsL)

Find indirect CX targets of MBONs

# Find the postsynaptic partners of MBON non-CX non-MBON targets
MBON_2ndaryPostConnections <- getConnectionTable(MBONnonCXTargets$bodyid,synapseType = "POST") ## Keeping the default threshold (3)

# Find the MBON secondary targets in the CX
MBONCX2ndaryTargetsL <- unique(MBON_2ndaryPostConnections$type.to)[unique(MBON_2ndaryPostConnections$type.to) %in% CXTypes$databaseType]
MBONCX2ndaryTargets <- getTypesTable(MBONCX2ndaryTargetsL)

Create and lateralize neuron bags of MBON primary and secondary CX targets

# Create neuron bags
# MBONTargetBag <- buildInputsOutputsByType(MBONCXTargets,slctROI=unique(roiSelection$roi))
# MBONnonCXTargetBag <- buildInputsOutputsByType(MBONnonCXTargets,slctROI=unique(roiSelection$roi))
# MBON2ndaryTargetBag <- buildInputsOutputsByType(MBONCX2ndaryTargets,slctROI=unique(roiSelection$roi))

# Separate left and right neurons in there as some tangential input regions might be cut on the left.
# MBONTargetBagLat <- lateralizeInputOutputList(MBONTargetBag)
# MBONnonCXTargetBagLat <- lateralizeInputOutputList(MBONnonCXTargetBag)
# MBON2ndaryTargetBagLat <- lateralizeInputOutputList(MBON2ndaryTargetBag)

# Use the functions in the neuprintrExtra package
MBONTargetBag <- neuronBag(MBONCXTargets,slctROI=unique(roiSelection$roi))
MBONTargetBagLat <- lateralize_types(MBONTargetBag)

MBONnonCXTargetBag <- neuronBag(MBONnonCXTargets,slctROI=unique(roiSelection$roi))
MBONnonCXTargetBagLat <- lateralize_types(MBONnonCXTargetBag)

Isolating the MBON primary targets to neurons on the right side

MBONTargetInp <- MBONTargetBagLat$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to))
# MBON2ndaryTargetInp <- MBON2ndaryTargetBagLat$inputs %>% filter((databaseType.from %in% MBONnonCXTargetsL) & grepl("_R",type.to))

Get MBON to CX_R indirect connection table through *_R

# Filter MBONnonCXTargetBagLat$input from MBONs to *_R
MBONnonCXTargetInp <- MBONnonCXTargetBagLat$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to))

# Filter MBONnonCXTargetBagLat$ouputs from MBONnonCXTargetInp$type.to to CX_R
MBONnonCXTargetR2CXRtab <- MBONnonCXTargetBagLat$outputs %>% filter(type.from %in% MBONnonCXTargetInp$type.to & databaseType.to %in% CXTypes$databaseType & grepl("_R",type.to))
# Filter MBONnonCXTargetInp by type.to with CX_R targets
MBON2nonCXTargetRtab <- MBONnonCXTargetInp %>% filter(type.to %in% MBONnonCXTargetR2CXRtab$type.from)

Combine all input regions into one (to get a “more fair” relative weight). Add a variable, totalMBContribution, that summarize how much of the total input the MBONs contribute to, and use it to sort the CX types.

MBONTargetInputCombined <- combineRois(MBONTargetBagLat,unique(MBONTargetInp$roi),newRoi="MBON outputs")
MBONTargetInpC <- MBONTargetInputCombined$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to)) %>% 
  group_by(roi,type.to) %>% mutate(totalMBContribution = sum(weightRelative)) %>% ungroup() %>% arrange(desc(totalMBContribution))  %>%
  mutate(type.to = factor(type.to,levels=unique(type.to)))## Filter MBON to right CX neurons (some of the left ones can be cut)

plotConnMat_MBON_CX_Romain <- plotConnectivityMatrix(MBONTargetInpC,byGroup="type")
print(plotConnMat_MBON_CX_Romain)
ggsave("MBON_CX_ConnectionsByType_weightRelative_Romain.eps", plot=plotConnMat_MBON_CX_Romain, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)


inputProportions <- ggplot(MBONTargetInpC,aes(x=type.to)) + geom_col(aes(y=weightRelative,fill=type.from)) + scale_fill_manual(values=typesPalette(unique(MBONTargetInpC$type.from)),name="MBON types",guide=guide_legend(ncol=2)) + theme_classic()  + theme(axis.text.x = element_text(angle = 90)) + labs(y="Relative weight in input regions",x="Type")
print(inputProportions)
ggsave("CXfromMBON_InputProportionsByType_weightRelative_Romain.eps", plot=inputProportions, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Combine all input regions and add the totalMBContribution variable for MBONnonCXTargetBagLat. This is then used to sort the MBONs to non-CX non-MBON targets_R table.

# Combine rois based on MBON2nonCXTargetRtab$roi and add a new roi for MBON outputs
MBON2nonCXTargetInputCombined <- combineRois(MBONnonCXTargetBagLat,unique(MBON2nonCXTargetRtab$roi),newRoi="MBON outputs")
# Sort MBON to non-CX non-MBON _R connections by totalMBContribution descending
MBON2nonCXTargetRInpCombSort <- MBON2nonCXTargetInputCombined$inputs %>% filter(startsWith(type.from,"MBON") & grepl("_R",type.to)) %>% 
  group_by(roi,type.to) %>% mutate(totalMBContribution = sum(weightRelative)) %>% ungroup() %>% arrange(desc(totalMBContribution))  %>%
  mutate(type.to = factor(type.to,levels=unique(type.to)))

# Filter MBON2nonCXTargetInputCombined$ouputs from MBON2nonCXTargetRInpCombSort$type.to to CX_R
MBONnonCXTargetR2CXRInpCombined <- MBON2nonCXTargetInputCombined$outputs %>% filter(type.from %in% MBON2nonCXTargetRInpCombSort$type.to & databaseType.to %in% CXTypes$databaseType & grepl("_R",type.to))
# Filter MBON2nonCXTargetRInpCombSort by type.to with CX_R targets
MBON2nonCXwCXRTgtInpCombSort <- MBON2nonCXTargetRInpCombSort %>% filter(type.to %in% MBONnonCXTargetR2CXRInpCombined$type.from)

plot_MBON2nonCXwCXRTgtInpCombSort <- plotConnectivityMatrix(MBON2nonCXwCXRTgtInpCombSort,byGroup="type")
print(plot_MBON2nonCXwCXRTgtInpCombSort)
ggsave("MBON2nonCXnonMBONTgtRwCXRTgt_InpCombinedSorted.eps", plot=plot_MBON2nonCXwCXRTgtInpCombSort, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)


inputProportions_MBON2nonCXwCXRTgtInpCombSort <- ggplot(MBON2nonCXwCXRTgtInpCombSort,aes(x=type.to)) + geom_col(aes(y=weightRelative,fill=type.from)) + scale_fill_manual(values=typesPalette(unique(MBON2nonCXwCXRTgtInpCombSort$type.from)),name="MBON types",guide=guide_legend(ncol=2)) + theme_classic()  + theme(axis.text.x = element_text(angle = 90)) + labs(y="Relative weight in input regions",x="Type")
47 levels in your palette, this is likely too many.
print(inputProportions_MBON2nonCXwCXRTgtInpCombSort)
ggsave("MBON2nonCXnonMBONTgtRwCXRTgt_InputProportionsCombinedSorted.eps", plot=inputProportions_MBON2nonCXwCXRTgtInpCombSort, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the MBON to CX_R indrect pathways

# Pull significant MBON2nonCXwCXRTgtInpCombSort connections based on "totalMBContribution"
StrongMBON2nonCXwCXRTgtInpCombSort <- MBON2nonCXwCXRTgtInpCombSort %>% filter(totalMBContribution > 0.05)
# Filter MBONnonCXTargetR2CXRInpCombined by type.from among StrongMBON2nonCXwCXRTgtInpCombSort$type.to and weightRelative > 0.01
StrongMBONnonCXTargetR2CXRtab <- MBONnonCXTargetR2CXRInpCombined %>% filter(type.from %in% StrongMBON2nonCXwCXRTgtInpCombSort$type.to & weightRelative > 0.01)
# Filter StrongMBON2nonCXwCXRTgtInpCombSort for those connecting to StrongMBONnonCXTargetR2CXRtab$type.from
StrongMBON2nonCXwCXRTgtInpCombSort <- StrongMBON2nonCXwCXRTgtInpCombSort %>% filter(type.to %in% StrongMBONnonCXTargetR2CXRtab$type.from)

# Make a combined table
StrongMBON2nonCXwCXRTgtTable <- StrongMBON2nonCXwCXRTgtInpCombSort %>% select(type.from,type.to,weightRelative)
StrongMBONnonCXTgtR2CXRTable <- StrongMBONnonCXTargetR2CXRtab %>% select(type.from,type.to,weightRelative)
StrongMBON2nonCXR2CXRTgtComboTable <- bind_rows(StrongMBON2nonCXwCXRTgtTable,StrongMBONnonCXTgtR2CXRTable, .id = NULL)

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from)), unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from)),
                  unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to))))
numTypes <- length(types)
numMBONs <- length(unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from)))
numMidNodes <- length(unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from)))
numCXtargets <- length(unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
                      y = c(seq(-1,1,length.out = numMBONs), seq(-1,1,length.out = numMidNodes), seq(-1,1,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
# StrongMBON2nonCXR2CXRTgtComboPath <- graphConTab_old(StrongMBON2nonCXR2CXRTgtComboTable,xyLookup,FALSE,TRUE)
StrongMBON2nonCXR2CXRTgtComboPath <- graphConTabPolyChrome(StrongMBON2nonCXR2CXRTgtComboTable,xyLookup,FALSE,TRUE)
StrongMBON2nonCXR2CXRTgtComboPath <- StrongMBON2nonCXR2CXRTgtComboPath + scale_y_reverse()
print(StrongMBON2nonCXR2CXRTgtComboPath)
ggsave("StrongMBON2nonCXR2CXRTgtComboPath.svg", plot=StrongMBON2nonCXR2CXRTgtComboPath, device="svg", path=PlotDir, scale=1, 
       width=30, height=45, units="in", dpi=300, limitsize=FALSE)


# Cluster the tables by type.from and type.to
StrongMBON2nonCXwCXRTgt_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2nonCXwCXRTgtTable,'StrongMBON2nonCXwCXRTgt_Type2Type')
Using weightRelative as value column: use value.var to override.

StrongMBONnonCXTgtR2CXR_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBONnonCXTgtR2CXRTable,'StrongMBONnonCXTgtR2CXR_Type2Type')
Using weightRelative as value column: use value.var to override.

# Make and plot cosine distance matrix
StrongMBON2nonCXwCXRTgtInpCombSort_CosDist <- cosDistClusterPlot(PlotDir,StrongMBON2nonCXwCXRTgtInpCombSort,"StrongMBON2nonCXwCXRTgtInpCombSort")
Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.

StrongMBONnonCXTargetR2CXR_CosDist <- cosDistClusterPlot(PlotDir,StrongMBONnonCXTargetR2CXRtab,"StrongMBONnonCXTargetR2CXR")
Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.

Test computing pathway weights

StrongMBON2nonCXR2CXRPath <- tables2path(StrongMBON2nonCXwCXRTgtInpCombSort,StrongMBONnonCXTargetR2CXRtab)

Check if there’re cohorts of pathway clusters

Find Strongly connected direct MBON targets in the CX

# Pull significant MBONTargetInpC connections based on "totalMBContribution"
StrongMBONTargetInpC <- MBONTargetInpC  %>% filter(totalMBContribution > 0.05)
StrongMBON2CX_TargetsType <- unique(as.vector(StrongMBONTargetInpC$databaseType.to))
StrongMBON2CX_TargetNeurons <- getTypesTable(StrongMBON2CX_TargetsType)
# head(StrongMBON2CX_TargetNeurons)

# Find the MBON bodyids
StrongMBON2CX_FromType <- unique(as.vector(StrongMBONTargetInpC$databaseType.from))
StrongMBON2CX_FromNeurons <- getTypesTable(StrongMBON2CX_FromType)
# head(StrongMBON2CX_FromNeurons)

# Find neuron to neuron connection table for strong direct MBON to CX connections
# StrongMBON2CX_ConnTable <- getConnectionTable_forSubset(StrongMBON2CX_FromNeurons$bodyid, StrongMBON2CX_TargetNeurons$bodyid) # getConnectionTable_forSubset() is not included in neuprintrExtra
StrongMBON2CX_ConnTable <- getConnectionTable(StrongMBON2CX_FromNeurons$bodyid,synapseType = "POST") ## Keeping the default threshold (3)
StrongMBON2CX_ConnTable <- StrongMBON2CX_ConnTable %>% filter(type.to %in% StrongMBON2CX_TargetsType)

# Arrange StrongMBON2CX_ConnTable in the order of StrongMBONTargetInpC$databaseType.to
StrongMBON2CX_ConnTable$type.to <- factor(StrongMBON2CX_ConnTable$type.to,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
StrongMBON2CX_ConnTable <- arrange(StrongMBON2CX_ConnTable,type.to)

# Plot StrongMBON2CX_ConnTable
plotStrongMBON2CX_ConnTable <- plotConnectivityMatrix(StrongMBON2CX_ConnTable,byGroup="id")
print(plotStrongMBON2CX_ConnTable)
ggsave("plotStrongMBON2CX_ConnTable.eps", plot=plotStrongMBON2CX_ConnTable, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the strong direct pathways from MBONs to CX

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBONTargetInpC$type.from)),unique(as.vector(StrongMBONTargetInpC$type.to))))
numFrom <- length(unique(as.vector(StrongMBONTargetInpC$type.from)))
numTo <- length(unique(as.vector(StrongMBONTargetInpC$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTo)), y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTo)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBONTargetInpCdf <- StrongMBONTargetInpC
StrongMBONTargetInpCdf$type.to <- as.vector(StrongMBONTargetInpCdf$type.to)
# gg_MBON2TargetTab <- graphConTab_old(StrongMBONTargetInpCdf,xyLookup,FALSE,TRUE)
gg_MBON2TargetTab <- graphConTabPolyChrome(StrongMBONTargetInpCdf,xyLookup,FALSE,TRUE)
gg_MBON2TargetTab <- gg_MBON2TargetTab + scale_y_reverse()
print(gg_MBON2TargetTab)
ggsave("StrongMBON2CXtargetsTab.svg", plot=gg_MBON2TargetTab, device="svg", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)


# Make and plot cosine distance matrix
StrongMBON2CXTargetR_Type2Type_CosDist <- cosDistClusterPlot(PlotDir,StrongMBONTargetInpC,"StrongMBON2CXTargetR_Type2Type")
Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.

# Use the neuprintrExtra functions. Still confusing???
# StrongMBON2CXR_Type2Type_cosClustByFrom <- connectivityCluster(inputsTable=StrongMBONTargetInpC,grouping="type") # cluster by type.from
# StrongMBON2CXR_Type2Type_cosClustByFrom <- setClusters(StrongMBON2CXR_Type2Type_cosClustByFrom,h=0.8)  # add a cluster.from column to StrongMBON2CXR_Type2Type_cosClustByFrom 
# plotClusters(StrongMBON2CXR_Type2Type_cosClustByFrom,colorAxis=T,h=0.8)

# StrongMBON2CXR_Type2Type_cosClustByTo <- connectivityCluster(outputsTable=StrongMBONTargetInpC,grouping="type") # cluster by type.to
# StrongMBON2CXR_Type2Type_cosClustByTo <- setClusters(StrongMBON2CXR_Type2Type_cosClustByTo,h=0.8)  # add a cluster.to column to StrongMBON2CXR_Type2Type_cosClustByTo
# plotClusters(StrongMBON2CXR_Type2Type_cosClustByTo,colorAxis=T,h=0.8)

# plotConnectivity(StrongMBON2CXR_Type2Type_cosClustByFrom, grouping="type", connectionMeasure="weightRelative", orderIn=StrongMBON2CXR_Type2Type_cosClustByFrom, orderOut=StrongMBON2CXR_Type2Type_cosClustByTo, facetInputs="cluster.from", facetOutputs="cluster.to", theme=theme_classic()) # + theme(panel.border = element_rect(colour = "grey", fill = NA, size=0.3))

Notes: 1) MBON05 has no branches ipsilateral to the cell body, only contralateral. FB4R only has dendrites ipsilateral to cell body. Therefore, only MBON05_L (not MBON05_R) makes synapses onto FB4R_R. 2) MBON09 has bilateral branches, but only makes contact with ipsilateral FB4R, i.e., MBON09_R onto FB4R_R. 3) MBON21 has bilateral branches, and synapses onto FB4R bilaterally. 4) MBON04 has bilateral branches, but more contralaterally relative to the name (although the *_L cell body is just off to the midline on the right side). 5) MBON12R and MBON13R only have branches ipsilateral to the cell body. 6) MBON03 has bilateral branches, but more contralaterally relative to the name. 7) MBON22 has ipsilateral branches in the calyx and bilateral branches in SIP/SMP. 8) MBON30 has bilateral branches, but more ipsilateral to the cell body.

Plot the combined direct and indirect pathways from MBONs to CX_R

# Combine MBON to CX_R direct and indirect tables 
StrongMBON2CXRTgtTable <- StrongMBONTargetInpCdf %>% select(type.from,type.to,weightRelative)
StrongMBON2CXRTgtDrctIndrctComboTable <- bind_rows(StrongMBON2CXRTgtTable,StrongMBON2nonCXR2CXRTgtComboTable, .id = NULL)

# Set up the layout for the combined pathway plot
MBONtypes <- unique(c(unique(as.vector(StrongMBON2CXRTgtTable$type.from)),unique(as.vector(StrongMBON2nonCXwCXRTgtTable$type.from))))
numMBONs <- length(MBONtypes)
midNodes <- unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.from))
numMidNodes <- length(midNodes)
allCXRtargets <- unique(c(unique(as.vector(StrongMBON2CXRTgtTable$type.to)),unique(as.vector(StrongMBONnonCXTgtR2CXRTable$type.to))))
numCXtargets <- length(allCXRtargets)
types <- unique(c(MBONtypes,midNodes,allCXRtargets))
numTypes <- length(types)

xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
                      y = c(seq(-1,1,length.out = numMBONs), seq(0,2,length.out = numMidNodes), seq(-1,1.5,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
# StrongMBON2CXRTgtDrctIndrctComboPath <- graphConTab_old(StrongMBON2CXRTgtDrctIndrctComboTable,xyLookup,FALSE,TRUE)
StrongMBON2CXRTgtDrctIndrctComboPath <- graphConTabPolyChrome(StrongMBON2CXRTgtDrctIndrctComboTable,xyLookup,FALSE,TRUE)
StrongMBON2CXRTgtDrctIndrctComboPath <- StrongMBON2CXRTgtDrctIndrctComboPath + scale_y_reverse()
print(StrongMBON2CXRTgtDrctIndrctComboPath)
ggsave("StrongMBON2CXRTgtDrctIndrctComboPath.svg", plot=StrongMBON2CXRTgtDrctIndrctComboPath, device="svg", path=PlotDir, scale=1, 
       width=30, height=90, units="in", dpi=300, limitsize=FALSE)

Find connections from strong MBON targets to secondary targets in CX

# Find the postsynaptic partners of StrongMBON2CX_TargetNeurons
# StrongMBON2CX_TargetsCnctTable <- getConnectionTable(StrongMBON2CX_TargetNeurons,synapseType="POST")
# head(StrongMBON2CX_TargetsCnctTable)
# StrongMBON2CXTargetsToPostInFB <- getConnectionTable_forSubset(StrongMBON2CX_TargetNeurons$bodyid, StrongMBON2CX_TargetsCnctTable$to,"FB")

# Combine strong direct and indirect MBON targets in the CX
StrongMBON2CXallTargetsType <- unique(c(StrongMBON2CX_TargetsType,unique(as.vector(StrongMBONnonCXTargetR2CXRtab$databaseType.to))))
StrongMBON2CXallTargetNeurons <- getTypesTable(StrongMBON2CXallTargetsType)
StrongMBON2CXTargetsToPostInFB <- getConnectionTable(StrongMBON2CXallTargetNeurons$bodyid,synapseType="POST",slctROI="FB")
# StrongMBON2CXTargetsToPostInFB <- getConnectionTable_forSubset(StrongMBON2CXallTargetNeurons$bodyid, StrongMBON2CX_TargetsCnctTable$to,"FB")

# Arrange StrongMBON2CXTargetsToPostInFB in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB$type.from <- factor(StrongMBON2CXTargetsToPostInFB$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB <- arrange(StrongMBON2CXTargetsToPostInFB,type.from)

# Sort StrongMBON2CXallTargetNeurons by pathway weight


# Plot and save the StrongMBON2CXTargetsToPostInFB based on the connectionMeasure of "weightRelative"
plotStrongMBON2CXTargetsToPostInFB <- plotConnectivityMatrix(StrongMBON2CXTargetsToPostInFB,byGroup="id")
print(plotStrongMBON2CXTargetsToPostInFB)
ggsave("plotStrongMBON2CXTargetsToPostInFB.eps", plot=plotStrongMBON2CXTargetsToPostInFB, device="eps", path=PlotDir, scale=1, 
       width=80, height=10, units="in", dpi=300, limitsize=FALSE)


# Get type to type connection table
StrongMBON2CXTargetsToPostInFB$type.from <- as.vector(StrongMBON2CXTargetsToPostInFB$type.from)
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- getTypeToTypeTable(StrongMBON2CXTargetsToPostInFB)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% arrange(type.from %in% StrongMBON2CX_TargetsType)

# Filter the table by weightRelative
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% filter(type.from != type.to)

# Arrange StrongMBON2CXTargetsToPostInFB_Type2TypeTab in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- factor(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- arrange(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,type.from)

# Plot and save the StrongMBON2CXTargetsToPostInFB_Type2TypeTab based on the connectionMeasure of "weightRelative"
plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn <- plotConnectivityMatrix(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn)
ggsave("StrongMBON2CXTargetsToPostInFB_Type2TypeTable_weightRelative.eps", plot=plotStrongMBON2CXTargetsToPostInFB_Type2TypeConn, device="eps", path=PlotDir, scale=1, 
       width=11, height=8, units="in", dpi=300, limitsize=TRUE)

Plot the pathways from strong MBON->CX targets to secondary CX targets

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)),sort(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.to)))  )   )
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.to)))

# Arrange the primary MBON->CX targets in a circle
angsFrom <- seq(-pi,pi,length.out = numFrom + 1)
angsFrom <- angsFrom[1:(length(angsFrom)-1)]
xFrom <- 1*sin(angsFrom)
yFrom <- 1*cos(angsFrom)

# Arrange the secondary MBON->CX targets in a bigger circle
angsTo <- seq(-pi,pi,length.out = numTypes - numFrom + 1)
angsTo <- angsTo[1:(length(angsTo)-1)]
xTo <-3*sin(angsTo)
yTo <-3*cos(angsTo)

# xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), 
#                      y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# xyLookup = data.frame(type = types, x = c(xFrom,rep(3,times = numTypes-numFrom)), 
#                      y = c(yFrom, seq(-8,8,length.out = numTypes-numFrom)))

xyLookup = data.frame(type = types, x = c(xFrom,xTo), y = c(yFrom,yTo))

# Graph the TypeToType ConnTable using the xyLookup lookupTable
StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from)
# gg_MBON1stTo2ndaryTargetTab <- graphConTab_old(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON1stTo2ndaryTargetTab <- graphConTabPolyChrome(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON1stTo2ndaryTargetTab <- gg_MBON1stTo2ndaryTargetTab + scale_y_reverse()
print(gg_MBON1stTo2ndaryTargetTab)
ggsave("MBON1stTo2ndaryTargetTab.svg", plot=gg_MBON1stTo2ndaryTargetTab, device="svg", path=PlotDir, scale=1, 
       width=16, height=16, units="in", dpi=300, limitsize=FALSE)

Cluster MB->CX primary targets by the correlation of their weightRelative connectivity to secondary targets and the inverse (clustering secondary targets based on their inputs)

StrongMBON2CXTargetsToPostInFB_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2CXTargetsToPostInFB_Type2TypeTab,'StrongMBON2CXTargetsToPostInFB_Type2Type')
Using weightRelative as value column: use value.var to override.

StrongMBON2CXTargetsToPostInFB_Type2TypeTab_hcByFrom <- StrongMBON2CXTargetsToPostInFB_Type2Type_clustered[[1]]
StrongMBON2CXTargetsToPostInFB_Type2TypeTab_hcByTo <- StrongMBON2CXTargetsToPostInFB_Type2Type_clustered[[2]]
rm(StrongMBON2CXTargetsToPostInFB_Type2Type_clustered);

# Make and plot cosine distance matrix
StrongMBON2CXTargetsToPostInFB_Type2Type_CosDist <- cosDistClusterPlot(PlotDir,StrongMBON2CXTargetsToPostInFB_Type2TypeTab,"StrongMBON2CXTargetsToPostInFB_Type2Type")
Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.

# Plot the connections by pathwayWeight (= totalMBContribution * weightRelative)

Find tertiary MBON targets in the CX

# Find the postsynaptic partners of secondary StrongMBON2CX_TargetNeurons
StrongMBON2CX_2ndTargetsInFB_Types <- unique(as.vector(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$databaseType.to))
StrongMBON2CX_2ndTargetsInFB_Neurons <- getTypesTable(StrongMBON2CX_2ndTargetsInFB_Types)
StrongMBON2CX_2ndTargetsToPostInFB <- getConnectionTable(StrongMBON2CX_2ndTargetsInFB_Neurons$bodyid,synapseType="POST",slctROI="FB")
# head(StrongMBON2CX_2ndTargetsToPostInFB)
# StrongMBON2CX_2ndTargetsToPostInFB <- getConnectionTable_forSubset(StrongMBON2CX_2ndTargetsInFB_Neurons$bodyid, StrongMBON2CX_2ndTargetsCnctTable$to,"FB")

# Arrange StrongMBON2CXTargetsToPostInFB in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB$type.from <- factor(StrongMBON2CXTargetsToPostInFB$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB <- arrange(StrongMBON2CXTargetsToPostInFB,type.from)

# Plot and save the StrongMBON2CX_2ndTargetsToPostInFB based on the connectionMeasure of "weightRelative"
plotStrongMBON2CX2ndTargetsToPostInFB <- plotConnectivityMatrix(StrongMBON2CX_2ndTargetsToPostInFB,byGroup="id")
print(plotStrongMBON2CX2ndTargetsToPostInFB)
ggsave("plotStrongMBON2CX2ndTargetsToPostInFB.eps", plot=plotStrongMBON2CX2ndTargetsToPostInFB, device="eps", path=PlotDir, scale=1, 
       width=180, height=80, units="in", dpi=300, limitsize=FALSE)


# Get type to type connection table
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- getTypeToTypeTable(StrongMBON2CX_2ndTargetsToPostInFB)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- StrongMBON2CXTargetsToPostInFB_Type2TypeTab %>% arrange(type.from %in% StrongMBON2CX_TargetsType)

# Filter the table by weightRelative
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab %>% filter(type.from != type.to)

# Arrange StrongMBON2CXTargetsToPostInFB_Type2TypeTab in the order of StrongMBONTargetInpC$databaseType.to
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from <- factor(StrongMBON2CXTargetsToPostInFB_Type2TypeTab$type.from,levels=unique(StrongMBONTargetInpC$databaseType.to),ordered=TRUE)
# StrongMBON2CXTargetsToPostInFB_Type2TypeTab <- arrange(StrongMBON2CXTargetsToPostInFB_Type2TypeTab,type.from)

# Plot and save the StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab based on the connectionMeasure of "weightRelative"
plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type <- plotConnectivityMatrix(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
print(plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type)
ggsave("StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTable_weightRelative.eps", plot=plotStrongMBON2CX2ndTargetsToPostInFB_Type2Type, device="eps", path=PlotDir, scale=1, 
       width=24, height=16, units="in", dpi=300, limitsize=FALSE)

Plot the pathways from strong MBON->CX secondary CX targets to tertiary CX targets

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.from)),sort(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.to)))  )   )
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab$type.to)))

# Arrange the secondary MBON->CX targets in a circle
angsFrom <- seq(-pi,pi,length.out = numFrom + 1)
angsFrom <- angsFrom[1:(length(angsFrom)-1)]
xFrom <- 4*sin(angsFrom)
yFrom <- 4*cos(angsFrom)

# Arrange the tertiary MBON->CX targets in a bigger circle
angsTo <- seq(-pi,pi,length.out = numTypes - numFrom + 1)
angsTo <- angsTo[1:(length(angsTo)-1)]
xTo <-5*sin(angsTo)
yTo <-5*cos(angsTo)

# xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), 
#                      y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# xyLookup = data.frame(type = types, x = c(xFrom,rep(3,times = numTypes-numFrom)), 
#                      y = c(yFrom, seq(-8,8,length.out = numTypes-numFrom)))

xyLookup = data.frame(type = types, x = c(xFrom,xTo), y = c(yFrom,yTo))

# Graph the TypeToType ConnTable using the xyLookup lookupTable
# gg_MBON2ndTo3rdTargetTab <- graphConTab_old(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON2ndTo3rdTargetTab <- graphConTabPolyChrome(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,xyLookup,FALSE,TRUE)
gg_MBON2ndTo3rdTargetTab <- gg_MBON2ndTo3rdTargetTab + scale_y_reverse()
print(gg_MBON2ndTo3rdTargetTab)
ggsave("MBON2ndTo3rdCXTargetTab.svg", plot=gg_MBON2ndTo3rdTargetTab, device="svg", path=PlotDir, scale=1, 
       width=30, height=30, units="in", dpi=300, limitsize=FALSE)

Cluster MB->CX secondary targets by the correlation of their weightRelative connectivity to tertiary targets and the inverse (clustering tertiary targets based on their inputs)

StrongMBON2CX_2ndTargetsToPostInFB_Type2Type_clustered <- plotCorrMatCluster(PlotDir,StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,'StrongMBON2CX_2ndTargetsToPostInFB_Type2Type')
Using weightRelative as value column: use value.var to override.

StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo <- StrongMBON2CX_2ndTargetsToPostInFB_Type2Type_clustered[[2]]

# Make and plot cosine distance matrix
StrongMBON2CX_2ndTargetsToPostInFB_Type2Type_CosDist <- cosDistClusterPlot(PlotDir,StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab,"StrongMBON2CX_2ndTargetsToPostInFB_Type2Type")
Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.Vectorized input to `element_text()` is not officially supported.
Results may be unexpected or may change in future versions of ggplot2.

Pathway plot based on the clustered and rearranged StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo

# Set up the layout for the pathway plot
types <- unique(c(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.from)),unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.to))))
numTypes <- length(types)
numFrom <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.from)))
numTo <- length(unique(as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo$type.to)))
xyLookup = data.frame(type = types, x = c(rep(-1,times = numFrom),rep(0,times = numTypes-numFrom)), y = c(seq(-1,1,length.out = numFrom), seq(-1,1,length.out = numTypes-numFrom)))

# Graph the TypeToType ConnTable using the lookupTable
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy$type.to <- as.vector(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy$type.to)
# StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway <- graphConTab_old(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy,xyLookup,FALSE,TRUE)
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway <- graphConTabPolyChrome(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_copy,xyLookup,FALSE,TRUE)
# gg_MBON2TargetTab <- graphConTabPolyChrome(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway,xyLookup,FALSE,TRUE)
StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway <- StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway + scale_y_reverse()
print(StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway)
ggsave("StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_clusterByTo_pathway.svg", plot=StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab_hcByTo_pathway, device="svg", path=PlotDir, scale=1, 
       width=30, height=60, units="in", dpi=300, limitsize=FALSE)

ran well up to here

put a break here

# Run t-SNE on the type.to of StrongMBON2CX_2ndTargetsToPostInFB_Type2TypeTab
# library(M3C)
# tsne(Data4Clust,perplex=15)

Check indirect connections from MBONs to the CX without lateralization

# Plot neuron to neuron connection matrix from MBONs to non-CX non-MBON targets
# MBON_nonCXnonMBON_ConnTable <- MBON_PostConnections %>% filter(type.to %in% MBONnonCXTargetsL)
# plotMBON_nonCXnonMBON_ConnTable <- plotConnectivityMatrix(MBON_nonCXnonMBON_ConnTable,byGroup="id",connectionMeasure="weightRelative")
# print(plotMBON_nonCXnonMBON_ConnTable)

# Plot type to type connection matrix from MBONs to non-CX non-MBON targets
# MBON_nonCXnonMBON_Type2TypeTab <- getTypeToTypeTable(MBON_nonCXnonMBON_ConnTable)
# Filter the table by weightRelative
# MBON_nonCXnonMBON_Type2TypeTab <- MBON_nonCXnonMBON_Type2TypeTab  %>% filter(weightRelative > 0.01)

# plotMBON_nonCXnonMBON_Type2TypeConn <- plotConnectivityMatrix(MBON_nonCXnonMBON_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
# print(plotMBON_nonCXnonMBON_Type2TypeConn)

# Plot neuron to neuron connection matrix from MBON non-CX non-MBON targets to their targets in the CX
# MBON_nonCXnonMBON_2ndaryCX_ConnTable <- MBON_2ndaryPostConnections %>% filter(type.to %in% MBONCX2ndaryTargetsL)
# plotMBON_nonCXnonMBON_2ndaryCX_ConnTable <- plotConnectivityMatrix(MBON_nonCXnonMBON_2ndaryCX_ConnTable,byGroup="id",connectionMeasure="weightRelative")
# print(plotMBON_nonCXnonMBON_2ndaryCX_ConnTable)

# Plot type to type connection matrix from MBON non-CX non-MBON targets to their targets in the CX
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- getTypeToTypeTable(MBON_nonCXnonMBON_2ndaryCX_ConnTable)
# Filter the table by weightRelative
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab  %>% filter(weightRelative > 0.01)
# Exclude connections to self types
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab %>% filter(type.from != type.to)

# plotMBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- plotConnectivityMatrix(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab,byGroup="type",connectionMeasure="weightRelative")
# print(plotMBON_nonCXnonMBON_2ndaryCX_Type2TypeTab)

# Filter MBON_nonCXnonMBON_Type2TypeTab based on type.to with direct connections in the CX
# MBON_nonCXnonMBONwCX2ndT_Type2TypeTab <- MBON_nonCXnonMBON_Type2TypeTab %>% filter(type.to %in% unique(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from))
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab %>% filter(type.from %in% unique(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.to))

# Plot pathways from MBONs to non-CX non-MBON targets to secondary CX targets
# Combine tables
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboTab <- bind_rows(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab,MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab, .id = NULL)

# Set up the layout for the pathway plot
# types <- unique(c(unique(as.vector(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.from)), unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from)),
#                  unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.to))))
# numTypes <- length(types)
# numMBONs <- length(unique(as.vector(MBON_nonCXnonMBONwCX2ndT_Type2TypeTab$type.from)))
# numMidNodes <- length(unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.from)))
# numCXtargets <- length(unique(as.vector(MBON_nonCXnonMBON_2ndaryCX_Type2TypeTab$type.to)))
# xyLookup = data.frame(type = types, x = c(rep(-1,times = numMBONs), rep(0,times = numMidNodes), rep(1,times = numCXtargets)), 
#                      y = c(seq(-1,1,length.out = numMBONs), seq(-1,1,length.out = numMidNodes), seq(-1,1,length.out = numCXtargets)))

# Graph the TypeToType ConnTable using the lookupTable
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath <- graphConTab_old(MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboTab,xyLookup,FALSE,TRUE)
# MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath <- MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath + scale_y_reverse()
# print(MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath)
# ggsave("MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath.svg", plot=MBON_nonCXnonMBON_2ndaryCX_Type2TypeComboPath, device="svg", path=PlotDir, scale=1, 
#       width=30, height=45, units="in", dpi=300, limitsize=FALSE)

Focus on the FB now (the only exception is LCNp which can probably be treated separately) and try to compute a “pathway weight” per CX target type. For every connection, MBONInfluence is the product of the relative weight with the total contribution of MBONs to the presynaptic neuron. pathwaysWeight is the sum of that over a target type (basically the sum of all its indirect MBON influences).

***Should also find pathways through middle neurons outside of the CX.

MBONTargetFBOut <- MBONTargetBagLat$outputs %>% filter(roi=="FB" & type.from %in% MBONTargetInpC$type.to) %>% 
  mutate(MBONInfluence = MBONTargetInpC$totalMBContribution[match(type.from,MBONTargetInpC$type.to)]*weightRelative) %>% group_by(type.to) %>% mutate(pathwaysWeight = sum(MBONInfluence)) %>% ungroup()
  

Looking at this table and sorting it according to various ways, the things that first pops out is that it’s mostly a FB tangential network (meaning most FB tangential neuron targets in the FB are other FB tangential neurons, I guess something reminiscent of the ring neurons). So we’d need a way to wean this recurrence in the way we consider which parts of the network are influenced by the MBONs.

MBONTargetFBOutSummary <- MBONTargetFBOut %>% group_by(type.to) %>% summarise(pathwaysWeight = pathwaysWeight[1])

Considering FB4R separately

MBONTargetFB4R <- MBONTargetFBOut %>% filter(type.from == "FB4R_R") %>% group_by(type.to) %>% summarise(pathwaysWeight = pathwaysWeight[1])

Suggestion: redo that, but study the pool of neurons influenced by a single MBON type at a time.

Other axis to explore: FB tangential inhomogeneous inputs onto columnar neurons.

LS0tCnRpdGxlOiAiTUJPTiBSZWR1eCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShuYXQpCmxpYnJhcnkobmV1cHJpbnRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBiYXBwbHkpCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShuZXVwcmludHJFeHRyYSkKbGlicmFyeShwYWxldHRlZXIpCmxpYnJhcnkoYWxwaGFodWxsKQoKIyBHbyB0byB0aGUgbmV1cnBpbnRSLW5vdGVib29rcyBkaXJlY3RvcnkgYmVmb3JlIGxvYWRpbmcgdGhlc2UgZnVuY3Rpb25zCnNvdXJjZSgidmlzdWFsaXplQ29ubmVjdGl2aXR5VGFibGVzLlIiKQojIHNvdXJjZSgibmV1cHJpbnRRdWVyeVV0aWxzLlIiKQpzb3VyY2UoIlIvdGFibGUyZ2dyYXBoVXRpbHMuUiIpCnNvdXJjZSgiUi9jb25uZWN0aXZpdHlNYXRyaWNlc1Rvb2xzLlIiKQojIHNvdXJjZSgiSW5wdXRPdXRwdXRCeVR5cGVVdGlscy5SIikKIyBzb3VyY2UoIlIvcm9pcy5SIikKIyBzb3VyY2UoIlIvc3VwZXJ0eXBlVXRpbHMuUiIpCiMgc291cmNlKCJjb2xvckNvZGVMb29rdXAuUiIpCnNvdXJjZSgicGF0aHdheXMuUiIpCnNvdXJjZSgiaW5wdXRPdXRwdXRSZWdpb25zVmlzLlIiKQpzb3VyY2UoIkdldE5ldXJvbnNJblJvaS5SIikKc291cmNlKCJGQk5ldHdvcmtWaXNVdGlscy5SIikKc291cmNlKCJDWF9Db250ZXh0dWFsQW5hbHlzaXNVdGlscy5SIikKIyBzb3VyY2UoIi9Vc2Vycy9kYW5jL3JlcG9zL25ldXByaW50ckV4dHJhL1IvbmV1cm9uQmFnLlIiKQoKb3B0aW9ucyhuYXQucGxvdGVuZ2luZSA9ICdyZ2wnKQpgYGAKCiMjIyBDb25maWd1cmFibGUgaW5wdXRzOiBjaG9vc2Ugc2F2ZS9wbG90IGRpcmVjdG9yaWVzCmBgYHtyfQojIERpcmVjdG9yeSB0byBzYXZlIGRhdGEgdG8uClNhdmVEaXIgPSAiL1VzZXJzL2RhbmMvRHJvcGJveCAoSEhNSSkvV29ya0Ryb3AvRGF0YS9GSUJTRU1fQW5hbHlzaXMiCgojIERpcmVjdG9yeSB0byBzYXZlIHBsb3RzIHRvLgpQbG90RGlyID0gIi9Vc2Vycy9kYW5jL0Ryb3Bib3ggKEhITUkpL1dvcmtEcm9wL0RhdGEvRklCU0VNX0FuYWx5c2lzIgpgYGAKCiMjIyBDcmVhdGUgc2F2ZS9wbG90IGRpcmVjdG9yeSBpZiB0aGV5IGRvbnQgZXhpc3RzIHlldApgYGB7cn0KaWYgKCFkaXIuZXhpc3RzKFNhdmVEaXIpKXtkaXIuY3JlYXRlKFNhdmVEaXIpfQppZiAoIWRpci5leGlzdHMoUGxvdERpcikpe2Rpci5jcmVhdGUoUGxvdERpcil9CmBgYAoKIyMjIENvbm5lY3QgdG8gbmV1cHJpbnQKYGBge3J9CiMgbmV1cHJpbnRfbG9naW4oKQpuZXVwcmludF9sb2dpbihjb25maWc9aHR0cjo6c2V0X2NvbmZpZyhodHRyOjpjb25maWcoc3NsX3ZlcmlmeXBlZXIgPSAwTCkpKQpgYGAKCiMjIyBDcmVhdGluZyBhIHJlbGF0aXZlbHkgaGlnaCBsZXZlbCBST0kgc2V0CmBgYHtyfQpyb2lzSCA8LSBnZXRSb2lUcmVlKCkKcm9pU2VsZWN0aW9uIDwtIHNlbGVjdFJvaVNldChyb2lzSCxkZWZhdWx0X2xldmVsPTEsZXhjZXB0aW9ucz1saXN0KCJDWCI9MikpCmBgYAoKIyMjIFB1bGxpbmcgbmV1cm9ucyBwb3N0c3luYXB0aWMgdG8gTUJPTnMKYGBge3J9Ck1CT05fTmFtZXMgPC0gbmV1cHJpbnRfc2VhcmNoKCJNQk9OLioiKQojIEZpbmQgdGhlIHBvc3RzeW5hcHRpYyBwYXJ0bmVycyBvZiBhbGwgTUJPTnMKTUJPTl9Qb3N0Q29ubmVjdGlvbnMgPC0gZ2V0Q29ubmVjdGlvblRhYmxlKE1CT05fTmFtZXMsc3luYXBzZVR5cGUgPSAiUE9TVCIpICMjIEtlZXBpbmcgdGhlIGRlZmF1bHQgdGhyZXNob2xkICgzKQpgYGAKCiMjIyBHZXQgTUJPTiB0YXJnZXRzIGluc2lkZSAvIG91dHNpZGUgb2YgQ1gKYGBge3J9CiMgU2VsZWN0aW5nIENYIHR5cGVzIGFzIHRob3NlIHByZXNlbnQgaW4gUm9tYWluJ3MgbGlzdC4KQ1hUeXBlcyA8LSBzdXBlcnR5cGUocmVhZF9jc3YoIi9Vc2Vycy9kYW5jL0Ryb3Bib3ggKEhITUkpL1dvcmtEcm9wL01hbnVzY3JpcHRzL0ZJQlNFTSBDWCBQYXBlciBKYW4gMjAyMC9DWC1jZWxsLXR5cGVzMDYwOTIwIikgJT4lIHJlbmFtZShkYXRhYmFzZVR5cGU9bi50eXBlKSkKTUJPTl90YXJnZXRzIDwtIHVuaXF1ZShNQk9OX1Bvc3RDb25uZWN0aW9ucyR0eXBlLnRvKQoKIyBQaWNrIE1CT04gdGFyZ2V0cyBpbiB0aGUgQ1gKTUJPTkNYVGFyZ2V0c0wgPC0gTUJPTl90YXJnZXRzW01CT05fdGFyZ2V0cyAlaW4lIENYVHlwZXMkZGF0YWJhc2VUeXBlXQpNQk9OQ1hUYXJnZXRzIDwtIGdldFR5cGVzVGFibGUoTUJPTkNYVGFyZ2V0c0wpCgojIFBpY2sgTUJPTiB0YXJnZXRzIG5vdCBpbiB0aGUgQ1gKTUJPTm5vbkNYVGFyZ2V0c0wgPC0gTUJPTl90YXJnZXRzWyEoTUJPTl90YXJnZXRzICVpbiUgQ1hUeXBlcyRkYXRhYmFzZVR5cGUpXQpNQk9Obm9uQ1hUYXJnZXRzTCA8LSBuYS5vbWl0KE1CT05ub25DWFRhcmdldHNMKQojIEV4Y2x1ZGUgTUJPTnMgZnJvbSB0aGlzIGxpc3QKTUJPTm5vbkNYVGFyZ2V0c0wgPC0gTUJPTm5vbkNYVGFyZ2V0c0xbIShNQk9Obm9uQ1hUYXJnZXRzTCAlaW4lIE1CT05fTmFtZXMkdHlwZSldCk1CT05ub25DWFRhcmdldHMgPC0gZ2V0VHlwZXNUYWJsZShNQk9Obm9uQ1hUYXJnZXRzTCkKYGBgCgojIyMgRmluZCBpbmRpcmVjdCBDWCB0YXJnZXRzIG9mIE1CT05zCmBgYHtyfQojIEZpbmQgdGhlIHBvc3RzeW5hcHRpYyBwYXJ0bmVycyBvZiBNQk9OIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzCk1CT05fMm5kYXJ5UG9zdENvbm5lY3Rpb25zIDwtIGdldENvbm5lY3Rpb25UYWJsZShNQk9Obm9uQ1hUYXJnZXRzJGJvZHlpZCxzeW5hcHNlVHlwZSA9ICJQT1NUIikgIyMgS2VlcGluZyB0aGUgZGVmYXVsdCB0aHJlc2hvbGQgKDMpCgojIEZpbmQgdGhlIE1CT04gc2Vjb25kYXJ5IHRhcmdldHMgaW4gdGhlIENYCk1CT05DWDJuZGFyeVRhcmdldHNMIDwtIHVuaXF1ZShNQk9OXzJuZGFyeVBvc3RDb25uZWN0aW9ucyR0eXBlLnRvKVt1bmlxdWUoTUJPTl8ybmRhcnlQb3N0Q29ubmVjdGlvbnMkdHlwZS50bykgJWluJSBDWFR5cGVzJGRhdGFiYXNlVHlwZV0KTUJPTkNYMm5kYXJ5VGFyZ2V0cyA8LSBnZXRUeXBlc1RhYmxlKE1CT05DWDJuZGFyeVRhcmdldHNMKQpgYGAKCiMjIyBDcmVhdGUgYW5kIGxhdGVyYWxpemUgbmV1cm9uIGJhZ3Mgb2YgTUJPTiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgQ1ggdGFyZ2V0cwpgYGB7cn0KIyBDcmVhdGUgbmV1cm9uIGJhZ3MKIyBNQk9OVGFyZ2V0QmFnIDwtIGJ1aWxkSW5wdXRzT3V0cHV0c0J5VHlwZShNQk9OQ1hUYXJnZXRzLHNsY3RST0k9dW5pcXVlKHJvaVNlbGVjdGlvbiRyb2kpKQojIE1CT05ub25DWFRhcmdldEJhZyA8LSBidWlsZElucHV0c091dHB1dHNCeVR5cGUoTUJPTm5vbkNYVGFyZ2V0cyxzbGN0Uk9JPXVuaXF1ZShyb2lTZWxlY3Rpb24kcm9pKSkKIyBNQk9OMm5kYXJ5VGFyZ2V0QmFnIDwtIGJ1aWxkSW5wdXRzT3V0cHV0c0J5VHlwZShNQk9OQ1gybmRhcnlUYXJnZXRzLHNsY3RST0k9dW5pcXVlKHJvaVNlbGVjdGlvbiRyb2kpKQoKIyBTZXBhcmF0ZSBsZWZ0IGFuZCByaWdodCBuZXVyb25zIGluIHRoZXJlIGFzIHNvbWUgdGFuZ2VudGlhbCBpbnB1dCByZWdpb25zIG1pZ2h0IGJlIGN1dCBvbiB0aGUgbGVmdC4KIyBNQk9OVGFyZ2V0QmFnTGF0IDwtIGxhdGVyYWxpemVJbnB1dE91dHB1dExpc3QoTUJPTlRhcmdldEJhZykKIyBNQk9Obm9uQ1hUYXJnZXRCYWdMYXQgPC0gbGF0ZXJhbGl6ZUlucHV0T3V0cHV0TGlzdChNQk9Obm9uQ1hUYXJnZXRCYWcpCiMgTUJPTjJuZGFyeVRhcmdldEJhZ0xhdCA8LSBsYXRlcmFsaXplSW5wdXRPdXRwdXRMaXN0KE1CT04ybmRhcnlUYXJnZXRCYWcpCgojIFVzZSB0aGUgZnVuY3Rpb25zIGluIHRoZSBuZXVwcmludHJFeHRyYSBwYWNrYWdlCk1CT05UYXJnZXRCYWcgPC0gbmV1cm9uQmFnKE1CT05DWFRhcmdldHMsc2xjdFJPST11bmlxdWUocm9pU2VsZWN0aW9uJHJvaSkpCk1CT05UYXJnZXRCYWdMYXQgPC0gbGF0ZXJhbGl6ZV90eXBlcyhNQk9OVGFyZ2V0QmFnKQoKTUJPTm5vbkNYVGFyZ2V0QmFnIDwtIG5ldXJvbkJhZyhNQk9Obm9uQ1hUYXJnZXRzLHNsY3RST0k9dW5pcXVlKHJvaVNlbGVjdGlvbiRyb2kpKQpNQk9Obm9uQ1hUYXJnZXRCYWdMYXQgPC0gbGF0ZXJhbGl6ZV90eXBlcyhNQk9Obm9uQ1hUYXJnZXRCYWcpCmBgYAoKIyMjIElzb2xhdGluZyB0aGUgTUJPTiBwcmltYXJ5IHRhcmdldHMgdG8gbmV1cm9ucyBvbiB0aGUgcmlnaHQgc2lkZQpgYGB7cn0KTUJPTlRhcmdldElucCA8LSBNQk9OVGFyZ2V0QmFnTGF0JGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKQojIE1CT04ybmRhcnlUYXJnZXRJbnAgPC0gTUJPTjJuZGFyeVRhcmdldEJhZ0xhdCRpbnB1dHMgJT4lIGZpbHRlcigoZGF0YWJhc2VUeXBlLmZyb20gJWluJSBNQk9Obm9uQ1hUYXJnZXRzTCkgJiBncmVwbCgiX1IiLHR5cGUudG8pKQpgYGAKCiMjIyBHZXQgTUJPTiB0byBDWF9SIGluZGlyZWN0IGNvbm5lY3Rpb24gdGFibGUgdGhyb3VnaCAqX1IKYGBge3J9CiMgRmlsdGVyIE1CT05ub25DWFRhcmdldEJhZ0xhdCRpbnB1dCBmcm9tIE1CT05zIHRvICpfUgpNQk9Obm9uQ1hUYXJnZXRJbnAgPC0gTUJPTm5vbkNYVGFyZ2V0QmFnTGF0JGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKQoKIyBGaWx0ZXIgTUJPTm5vbkNYVGFyZ2V0QmFnTGF0JG91cHV0cyBmcm9tIE1CT05ub25DWFRhcmdldElucCR0eXBlLnRvIHRvIENYX1IKTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIgPC0gTUJPTm5vbkNYVGFyZ2V0QmFnTGF0JG91dHB1dHMgJT4lIGZpbHRlcih0eXBlLmZyb20gJWluJSBNQk9Obm9uQ1hUYXJnZXRJbnAkdHlwZS50byAmIGRhdGFiYXNlVHlwZS50byAlaW4lIENYVHlwZXMkZGF0YWJhc2VUeXBlICYgZ3JlcGwoIl9SIix0eXBlLnRvKSkKIyBGaWx0ZXIgTUJPTm5vbkNYVGFyZ2V0SW5wIGJ5IHR5cGUudG8gd2l0aCBDWF9SIHRhcmdldHMKTUJPTjJub25DWFRhcmdldFJ0YWIgPC0gTUJPTm5vbkNYVGFyZ2V0SW5wICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIE1CT05ub25DWFRhcmdldFIyQ1hSdGFiJHR5cGUuZnJvbSkKYGBgCgojIyMgQ29tYmluZSBhbGwgaW5wdXQgcmVnaW9ucyBpbnRvIG9uZSAodG8gZ2V0IGEgIm1vcmUgZmFpciIgcmVsYXRpdmUgd2VpZ2h0KS4gQWRkIGEgdmFyaWFibGUsIHRvdGFsTUJDb250cmlidXRpb24sIHRoYXQgc3VtbWFyaXplIGhvdyBtdWNoIG9mIHRoZSB0b3RhbCBpbnB1dCB0aGUgTUJPTnMgY29udHJpYnV0ZSB0bywgYW5kIHVzZSBpdCB0byBzb3J0IHRoZSBDWCB0eXBlcy4KYGBge3J9Ck1CT05UYXJnZXRJbnB1dENvbWJpbmVkIDwtIGNvbWJpbmVSb2lzKE1CT05UYXJnZXRCYWdMYXQsdW5pcXVlKE1CT05UYXJnZXRJbnAkcm9pKSxuZXdSb2k9Ik1CT04gb3V0cHV0cyIpCk1CT05UYXJnZXRJbnBDIDwtIE1CT05UYXJnZXRJbnB1dENvbWJpbmVkJGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKSAlPiUgCiAgZ3JvdXBfYnkocm9pLHR5cGUudG8pICU+JSBtdXRhdGUodG90YWxNQkNvbnRyaWJ1dGlvbiA9IHN1bSh3ZWlnaHRSZWxhdGl2ZSkpICU+JSB1bmdyb3VwKCkgJT4lIGFycmFuZ2UoZGVzYyh0b3RhbE1CQ29udHJpYnV0aW9uKSkgICU+JQogIG11dGF0ZSh0eXBlLnRvID0gZmFjdG9yKHR5cGUudG8sbGV2ZWxzPXVuaXF1ZSh0eXBlLnRvKSkpIyMgRmlsdGVyIE1CT04gdG8gcmlnaHQgQ1ggbmV1cm9ucyAoc29tZSBvZiB0aGUgbGVmdCBvbmVzIGNhbiBiZSBjdXQpCgpwbG90Q29ubk1hdF9NQk9OX0NYX1JvbWFpbiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05UYXJnZXRJbnBDLGJ5R3JvdXA9InR5cGUiKQpwcmludChwbG90Q29ubk1hdF9NQk9OX0NYX1JvbWFpbikKZ2dzYXZlKCJNQk9OX0NYX0Nvbm5lY3Rpb25zQnlUeXBlX3dlaWdodFJlbGF0aXZlX1JvbWFpbi5lcHMiLCBwbG90PXBsb3RDb25uTWF0X01CT05fQ1hfUm9tYWluLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQoKaW5wdXRQcm9wb3J0aW9ucyA8LSBnZ3Bsb3QoTUJPTlRhcmdldElucEMsYWVzKHg9dHlwZS50bykpICsgZ2VvbV9jb2woYWVzKHk9d2VpZ2h0UmVsYXRpdmUsZmlsbD10eXBlLmZyb20pKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz10eXBlc1BhbGV0dGUodW5pcXVlKE1CT05UYXJnZXRJbnBDJHR5cGUuZnJvbSkpLG5hbWU9Ik1CT04gdHlwZXMiLGd1aWRlPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKyBsYWJzKHk9IlJlbGF0aXZlIHdlaWdodCBpbiBpbnB1dCByZWdpb25zIix4PSJUeXBlIikKcHJpbnQoaW5wdXRQcm9wb3J0aW9ucykKZ2dzYXZlKCJDWGZyb21NQk9OX0lucHV0UHJvcG9ydGlvbnNCeVR5cGVfd2VpZ2h0UmVsYXRpdmVfUm9tYWluLmVwcyIsIHBsb3Q9aW5wdXRQcm9wb3J0aW9ucywgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKYGBgCgojIyMgQ29tYmluZSBhbGwgaW5wdXQgcmVnaW9ucyBhbmQgYWRkIHRoZSB0b3RhbE1CQ29udHJpYnV0aW9uIHZhcmlhYmxlIGZvciBNQk9Obm9uQ1hUYXJnZXRCYWdMYXQuIFRoaXMgaXMgdGhlbiB1c2VkIHRvIHNvcnQgdGhlIE1CT05zIHRvIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzX1IgdGFibGUuIApgYGB7cn0KIyBDb21iaW5lIHJvaXMgYmFzZWQgb24gTUJPTjJub25DWFRhcmdldFJ0YWIkcm9pIGFuZCBhZGQgYSBuZXcgcm9pIGZvciBNQk9OIG91dHB1dHMKTUJPTjJub25DWFRhcmdldElucHV0Q29tYmluZWQgPC0gY29tYmluZVJvaXMoTUJPTm5vbkNYVGFyZ2V0QmFnTGF0LHVuaXF1ZShNQk9OMm5vbkNYVGFyZ2V0UnRhYiRyb2kpLG5ld1JvaT0iTUJPTiBvdXRwdXRzIikKIyBTb3J0IE1CT04gdG8gbm9uLUNYIG5vbi1NQk9OIF9SIGNvbm5lY3Rpb25zIGJ5IHRvdGFsTUJDb250cmlidXRpb24gZGVzY2VuZGluZwpNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0IDwtIE1CT04ybm9uQ1hUYXJnZXRJbnB1dENvbWJpbmVkJGlucHV0cyAlPiUgZmlsdGVyKHN0YXJ0c1dpdGgodHlwZS5mcm9tLCJNQk9OIikgJiBncmVwbCgiX1IiLHR5cGUudG8pKSAlPiUgCiAgZ3JvdXBfYnkocm9pLHR5cGUudG8pICU+JSBtdXRhdGUodG90YWxNQkNvbnRyaWJ1dGlvbiA9IHN1bSh3ZWlnaHRSZWxhdGl2ZSkpICU+JSB1bmdyb3VwKCkgJT4lIGFycmFuZ2UoZGVzYyh0b3RhbE1CQ29udHJpYnV0aW9uKSkgICU+JQogIG11dGF0ZSh0eXBlLnRvID0gZmFjdG9yKHR5cGUudG8sbGV2ZWxzPXVuaXF1ZSh0eXBlLnRvKSkpCgojIEZpbHRlciBNQk9OMm5vbkNYVGFyZ2V0SW5wdXRDb21iaW5lZCRvdXB1dHMgZnJvbSBNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0JHR5cGUudG8gdG8gQ1hfUgpNQk9Obm9uQ1hUYXJnZXRSMkNYUklucENvbWJpbmVkIDwtIE1CT04ybm9uQ1hUYXJnZXRJbnB1dENvbWJpbmVkJG91dHB1dHMgJT4lIGZpbHRlcih0eXBlLmZyb20gJWluJSBNQk9OMm5vbkNYVGFyZ2V0UklucENvbWJTb3J0JHR5cGUudG8gJiBkYXRhYmFzZVR5cGUudG8gJWluJSBDWFR5cGVzJGRhdGFiYXNlVHlwZSAmIGdyZXBsKCJfUiIsdHlwZS50bykpCiMgRmlsdGVyIE1CT04ybm9uQ1hUYXJnZXRSSW5wQ29tYlNvcnQgYnkgdHlwZS50byB3aXRoIENYX1IgdGFyZ2V0cwpNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IDwtIE1CT04ybm9uQ1hUYXJnZXRSSW5wQ29tYlNvcnQgJT4lIGZpbHRlcih0eXBlLnRvICVpbiUgTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCR0eXBlLmZyb20pCgpwbG90X01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0LGJ5R3JvdXA9InR5cGUiKQpwcmludChwbG90X01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQpCmdnc2F2ZSgiTUJPTjJub25DWG5vbk1CT05UZ3RSd0NYUlRndF9JbnBDb21iaW5lZFNvcnRlZC5lcHMiLCBwbG90PXBsb3RfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKCmlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCA8LSBnZ3Bsb3QoTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCxhZXMoeD10eXBlLnRvKSkgKyBnZW9tX2NvbChhZXMoeT13ZWlnaHRSZWxhdGl2ZSxmaWxsPXR5cGUuZnJvbSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXR5cGVzUGFsZXR0ZSh1bmlxdWUoTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCR0eXBlLmZyb20pKSxuYW1lPSJNQk9OIHR5cGVzIixndWlkZT1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKyB0aGVtZV9jbGFzc2ljKCkgICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsgbGFicyh5PSJSZWxhdGl2ZSB3ZWlnaHQgaW4gaW5wdXQgcmVnaW9ucyIseD0iVHlwZSIpCnByaW50KGlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCkKZ2dzYXZlKCJNQk9OMm5vbkNYbm9uTUJPTlRndFJ3Q1hSVGd0X0lucHV0UHJvcG9ydGlvbnNDb21iaW5lZFNvcnRlZC5lcHMiLCBwbG90PWlucHV0UHJvcG9ydGlvbnNfTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MTEsIGhlaWdodD04LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9VFJVRSkKYGBgCgojIyMgUGxvdCB0aGUgTUJPTiB0byBDWF9SIGluZHJlY3QgcGF0aHdheXMKYGBge3J9CiMgUHVsbCBzaWduaWZpY2FudCBNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IGNvbm5lY3Rpb25zIGJhc2VkIG9uICJ0b3RhbE1CQ29udHJpYnV0aW9uIgpTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0IDwtIE1CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQgJT4lIGZpbHRlcih0b3RhbE1CQ29udHJpYnV0aW9uID4gMC4wNSkKIyBGaWx0ZXIgTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCBieSB0eXBlLmZyb20gYW1vbmcgU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCR0eXBlLnRvIGFuZCB3ZWlnaHRSZWxhdGl2ZSA+IDAuMDEKU3Ryb25nTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIgPC0gTUJPTm5vbkNYVGFyZ2V0UjJDWFJJbnBDb21iaW5lZCAlPiUgZmlsdGVyKHR5cGUuZnJvbSAlaW4lIFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQkdHlwZS50byAmIHdlaWdodFJlbGF0aXZlID4gMC4wMSkKIyBGaWx0ZXIgU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCBmb3IgdGhvc2UgY29ubmVjdGluZyB0byBTdHJvbmdNQk9Obm9uQ1hUYXJnZXRSMkNYUnRhYiR0eXBlLmZyb20KU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCA8LSBTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0ICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIFN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSdGFiJHR5cGUuZnJvbSkKCiMgTWFrZSBhIGNvbWJpbmVkIHRhYmxlClN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUgPC0gU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCAlPiUgc2VsZWN0KHR5cGUuZnJvbSx0eXBlLnRvLHdlaWdodFJlbGF0aXZlKQpTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlIDwtIFN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSdGFiICU+JSBzZWxlY3QodHlwZS5mcm9tLHR5cGUudG8sd2VpZ2h0UmVsYXRpdmUpClN0cm9uZ01CT04ybm9uQ1hSMkNYUlRndENvbWJvVGFibGUgPC0gYmluZF9yb3dzKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUsU3Ryb25nTUJPTm5vbkNYVGd0UjJDWFJUYWJsZSwgLmlkID0gTlVMTCkKCiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBwYXRod2F5IHBsb3QKdHlwZXMgPC0gdW5pcXVlKGModW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMm5vbkNYd0NYUlRndFRhYmxlJHR5cGUuZnJvbSkpLCB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSksCiAgICAgICAgICAgICAgICAgIHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTm5vbkNYVGd0UjJDWFJUYWJsZSR0eXBlLnRvKSkpKQpudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCm51bU1CT05zIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUkdHlwZS5mcm9tKSkpCm51bU1pZE5vZGVzIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSkpCm51bUNYdGFyZ2V0cyA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlJHR5cGUudG8pKSkKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtTUJPTnMpLCByZXAoMCx0aW1lcyA9IG51bU1pZE5vZGVzKSwgcmVwKDEsdGltZXMgPSBudW1DWHRhcmdldHMpKSwgCiAgICAgICAgICAgICAgICAgICAgICB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTUJPTnMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtTWlkTm9kZXMpLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtQ1h0YXJnZXRzKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKIyBTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1BhdGggPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04ybm9uQ1hSMkNYUlRndENvbWJvVGFibGUseHlMb29rdXAsRkFMU0UsVFJVRSkKU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoIDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1RhYmxlLHh5TG9va3VwLEZBTFNFLFRSVUUpClN0cm9uZ01CT04ybm9uQ1hSMkNYUlRndENvbWJvUGF0aCA8LSBTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1BhdGggKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChTdHJvbmdNQk9OMm5vbkNYUjJDWFJUZ3RDb21ib1BhdGgpCmdnc2F2ZSgiU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoLnN2ZyIsIHBsb3Q9U3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9QYXRoLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0zMCwgaGVpZ2h0PTQ1LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCgojIENsdXN0ZXIgdGhlIHRhYmxlcyBieSB0eXBlLmZyb20gYW5kIHR5cGUudG8KU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RfVHlwZTJUeXBlX2NsdXN0ZXJlZCA8LSBwbG90Q29yck1hdENsdXN0ZXIoUGxvdERpcixTdHJvbmdNQk9OMm5vbkNYd0NYUlRndFRhYmxlLCdTdHJvbmdNQk9OMm5vbkNYd0NYUlRndF9UeXBlMlR5cGUnKQpTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUl9UeXBlMlR5cGVfY2x1c3RlcmVkIDwtIHBsb3RDb3JyTWF0Q2x1c3RlcihQbG90RGlyLFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUsJ1N0cm9uZ01CT05ub25DWFRndFIyQ1hSX1R5cGUyVHlwZScpCgojIE1ha2UgYW5kIHBsb3QgY29zaW5lIGRpc3RhbmNlIG1hdHJpeApTdHJvbmdNQk9OMm5vbkNYd0NYUlRndElucENvbWJTb3J0X0Nvc0Rpc3QgPC0gY29zRGlzdENsdXN0ZXJQbG90KFBsb3REaXIsU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCwiU3Ryb25nTUJPTjJub25DWHdDWFJUZ3RJbnBDb21iU29ydCIpClN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSX0Nvc0Rpc3QgPC0gY29zRGlzdENsdXN0ZXJQbG90KFBsb3REaXIsU3Ryb25nTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIsIlN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSIikKYGBgCgojIyMgVGVzdCBjb21wdXRpbmcgcGF0aHdheSB3ZWlnaHRzCmBgYHtyfQpTdHJvbmdNQk9OMm5vbkNYUjJDWFJQYXRoIDwtIHRhYmxlczJwYXRoKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0SW5wQ29tYlNvcnQsU3Ryb25nTUJPTm5vbkNYVGFyZ2V0UjJDWFJ0YWIpCmBgYAoKIyMjIENoZWNrIGlmIHRoZXJlJ3JlIGNvaG9ydHMgb2YgcGF0aHdheSBjbHVzdGVycwpgYGB7cn0KCmBgYAoKIyMjIEZpbmQgU3Ryb25nbHkgY29ubmVjdGVkIGRpcmVjdCBNQk9OIHRhcmdldHMgaW4gdGhlIENYCmBgYHtyfQojIFB1bGwgc2lnbmlmaWNhbnQgTUJPTlRhcmdldElucEMgY29ubmVjdGlvbnMgYmFzZWQgb24gInRvdGFsTUJDb250cmlidXRpb24iClN0cm9uZ01CT05UYXJnZXRJbnBDIDwtIE1CT05UYXJnZXRJbnBDICAlPiUgZmlsdGVyKHRvdGFsTUJDb250cmlidXRpb24gPiAwLjA1KQpTdHJvbmdNQk9OMkNYX1RhcmdldHNUeXBlIDwtIHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvKSkKU3Ryb25nTUJPTjJDWF9UYXJnZXROZXVyb25zIDwtIGdldFR5cGVzVGFibGUoU3Ryb25nTUJPTjJDWF9UYXJnZXRzVHlwZSkKIyBoZWFkKFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucykKCiMgRmluZCB0aGUgTUJPTiBib2R5aWRzClN0cm9uZ01CT04yQ1hfRnJvbVR5cGUgPC0gdW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUuZnJvbSkpClN0cm9uZ01CT04yQ1hfRnJvbU5ldXJvbnMgPC0gZ2V0VHlwZXNUYWJsZShTdHJvbmdNQk9OMkNYX0Zyb21UeXBlKQojIGhlYWQoU3Ryb25nTUJPTjJDWF9Gcm9tTmV1cm9ucykKCiMgRmluZCBuZXVyb24gdG8gbmV1cm9uIGNvbm5lY3Rpb24gdGFibGUgZm9yIHN0cm9uZyBkaXJlY3QgTUJPTiB0byBDWCBjb25uZWN0aW9ucwojIFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlIDwtIGdldENvbm5lY3Rpb25UYWJsZV9mb3JTdWJzZXQoU3Ryb25nTUJPTjJDWF9Gcm9tTmV1cm9ucyRib2R5aWQsIFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucyRib2R5aWQpICMgZ2V0Q29ubmVjdGlvblRhYmxlX2ZvclN1YnNldCgpIGlzIG5vdCBpbmNsdWRlZCBpbiBuZXVwcmludHJFeHRyYQpTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSA8LSBnZXRDb25uZWN0aW9uVGFibGUoU3Ryb25nTUJPTjJDWF9Gcm9tTmV1cm9ucyRib2R5aWQsc3luYXBzZVR5cGUgPSAiUE9TVCIpICMjIEtlZXBpbmcgdGhlIGRlZmF1bHQgdGhyZXNob2xkICgzKQpTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSA8LSBTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSAlPiUgZmlsdGVyKHR5cGUudG8gJWluJSBTdHJvbmdNQk9OMkNYX1RhcmdldHNUeXBlKQoKIyBBcnJhbmdlIFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlIGluIHRoZSBvcmRlciBvZiBTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8KU3Ryb25nTUJPTjJDWF9Db25uVGFibGUkdHlwZS50byA8LSBmYWN0b3IoU3Ryb25nTUJPTjJDWF9Db25uVGFibGUkdHlwZS50byxsZXZlbHM9dW5pcXVlKFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50byksb3JkZXJlZD1UUlVFKQpTdHJvbmdNQk9OMkNYX0Nvbm5UYWJsZSA8LSBhcnJhbmdlKFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlLHR5cGUudG8pCgojIFBsb3QgU3Ryb25nTUJPTjJDWF9Db25uVGFibGUKcGxvdFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlIDwtIHBsb3RDb25uZWN0aXZpdHlNYXRyaXgoU3Ryb25nTUJPTjJDWF9Db25uVGFibGUsYnlHcm91cD0iaWQiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWF9Db25uVGFibGUpCmdnc2F2ZSgicGxvdFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlLmVwcyIsIHBsb3Q9cGxvdFN0cm9uZ01CT04yQ1hfQ29ublRhYmxlLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQpgYGAKCiMjIyBQbG90IHRoZSBzdHJvbmcgZGlyZWN0IHBhdGh3YXlzIGZyb20gTUJPTnMgdG8gQ1gKYGBge3J9CiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBwYXRod2F5IHBsb3QKdHlwZXMgPC0gdW5pcXVlKGModW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OVGFyZ2V0SW5wQyR0eXBlLmZyb20pKSx1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05UYXJnZXRJbnBDJHR5cGUudG8pKSkpCm51bUZyb20gPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTlRhcmdldElucEMkdHlwZS5mcm9tKSkpCm51bVRvIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05UYXJnZXRJbnBDJHR5cGUudG8pKSkKeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtRnJvbSkscmVwKDAsdGltZXMgPSBudW1UbykpLCB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtRnJvbSksIHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1UbykpKQoKIyBHcmFwaCB0aGUgVHlwZVRvVHlwZSBDb25uVGFibGUgdXNpbmcgdGhlIGxvb2t1cFRhYmxlClN0cm9uZ01CT05UYXJnZXRJbnBDZGYgPC0gU3Ryb25nTUJPTlRhcmdldElucEMKU3Ryb25nTUJPTlRhcmdldElucENkZiR0eXBlLnRvIDwtIGFzLnZlY3RvcihTdHJvbmdNQk9OVGFyZ2V0SW5wQ2RmJHR5cGUudG8pCiMgZ2dfTUJPTjJUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT05UYXJnZXRJbnBDZGYseHlMb29rdXAsRkFMU0UsVFJVRSkKZ2dfTUJPTjJUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJQb2x5Q2hyb21lKFN0cm9uZ01CT05UYXJnZXRJbnBDZGYseHlMb29rdXAsRkFMU0UsVFJVRSkKZ2dfTUJPTjJUYXJnZXRUYWIgPC0gZ2dfTUJPTjJUYXJnZXRUYWIgKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChnZ19NQk9OMlRhcmdldFRhYikKZ2dzYXZlKCJTdHJvbmdNQk9OMkNYdGFyZ2V0c1RhYi5zdmciLCBwbG90PWdnX01CT04yVGFyZ2V0VGFiLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQoKIyBNYWtlIGFuZCBwbG90IGNvc2luZSBkaXN0YW5jZSBtYXRyaXgKU3Ryb25nTUJPTjJDWFRhcmdldFJfVHlwZTJUeXBlX0Nvc0Rpc3QgPC0gY29zRGlzdENsdXN0ZXJQbG90KFBsb3REaXIsU3Ryb25nTUJPTlRhcmdldElucEMsIlN0cm9uZ01CT04yQ1hUYXJnZXRSX1R5cGUyVHlwZSIpCgojIFVzZSB0aGUgbmV1cHJpbnRyRXh0cmEgZnVuY3Rpb25zLiBTdGlsbCBjb25mdXNpbmc/Pz8KIyBTdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeUZyb20gPC0gY29ubmVjdGl2aXR5Q2x1c3RlcihpbnB1dHNUYWJsZT1TdHJvbmdNQk9OVGFyZ2V0SW5wQyxncm91cGluZz0idHlwZSIpICMgY2x1c3RlciBieSB0eXBlLmZyb20KIyBTdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeUZyb20gPC0gc2V0Q2x1c3RlcnMoU3Ryb25nTUJPTjJDWFJfVHlwZTJUeXBlX2Nvc0NsdXN0QnlGcm9tLGg9MC44KSAgIyBhZGQgYSBjbHVzdGVyLmZyb20gY29sdW1uIHRvIFN0cm9uZ01CT04yQ1hSX1R5cGUyVHlwZV9jb3NDbHVzdEJ5RnJvbSAKIyBwbG90Q2x1c3RlcnMoU3Ryb25nTUJPTjJDWFJfVHlwZTJUeXBlX2Nvc0NsdXN0QnlGcm9tLGNvbG9yQXhpcz1ULGg9MC44KQoKIyBTdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeVRvIDwtIGNvbm5lY3Rpdml0eUNsdXN0ZXIob3V0cHV0c1RhYmxlPVN0cm9uZ01CT05UYXJnZXRJbnBDLGdyb3VwaW5nPSJ0eXBlIikgIyBjbHVzdGVyIGJ5IHR5cGUudG8KIyBTdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeVRvIDwtIHNldENsdXN0ZXJzKFN0cm9uZ01CT04yQ1hSX1R5cGUyVHlwZV9jb3NDbHVzdEJ5VG8saD0wLjgpICAjIGFkZCBhIGNsdXN0ZXIudG8gY29sdW1uIHRvIFN0cm9uZ01CT04yQ1hSX1R5cGUyVHlwZV9jb3NDbHVzdEJ5VG8KIyBwbG90Q2x1c3RlcnMoU3Ryb25nTUJPTjJDWFJfVHlwZTJUeXBlX2Nvc0NsdXN0QnlUbyxjb2xvckF4aXM9VCxoPTAuOCkKCiMgcGxvdENvbm5lY3Rpdml0eShTdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeUZyb20sIGdyb3VwaW5nPSJ0eXBlIiwgY29ubmVjdGlvbk1lYXN1cmU9IndlaWdodFJlbGF0aXZlIiwgb3JkZXJJbj1TdHJvbmdNQk9OMkNYUl9UeXBlMlR5cGVfY29zQ2x1c3RCeUZyb20sIG9yZGVyT3V0PVN0cm9uZ01CT04yQ1hSX1R5cGUyVHlwZV9jb3NDbHVzdEJ5VG8sIGZhY2V0SW5wdXRzPSJjbHVzdGVyLmZyb20iLCBmYWNldE91dHB1dHM9ImNsdXN0ZXIudG8iLCB0aGVtZT10aGVtZV9jbGFzc2ljKCkpICMgKyB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImdyZXkiLCBmaWxsID0gTkEsIHNpemU9MC4zKSkKCmBgYApOb3RlczogCjEpIE1CT04wNSBoYXMgbm8gYnJhbmNoZXMgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keSwgb25seSBjb250cmFsYXRlcmFsLiBGQjRSIG9ubHkgaGFzIGRlbmRyaXRlcyBpcHNpbGF0ZXJhbCB0byBjZWxsIGJvZHkuIFRoZXJlZm9yZSwgb25seSBNQk9OMDVfTCAobm90IE1CT04wNV9SKSBtYWtlcyBzeW5hcHNlcyBvbnRvIEZCNFJfUi4KMikgTUJPTjA5IGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGJ1dCBvbmx5IG1ha2VzIGNvbnRhY3Qgd2l0aCBpcHNpbGF0ZXJhbCBGQjRSLCBpLmUuLCBNQk9OMDlfUiBvbnRvIEZCNFJfUi4KMykgTUJPTjIxIGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGFuZCBzeW5hcHNlcyBvbnRvIEZCNFIgYmlsYXRlcmFsbHkuCjQpIE1CT04wNCBoYXMgYmlsYXRlcmFsIGJyYW5jaGVzLCBidXQgbW9yZSBjb250cmFsYXRlcmFsbHkgcmVsYXRpdmUgdG8gdGhlIG5hbWUgKGFsdGhvdWdoIHRoZSAqX0wgY2VsbCBib2R5IGlzIGp1c3Qgb2ZmIHRvIHRoZSBtaWRsaW5lIG9uIHRoZSByaWdodCBzaWRlKS4KNSkgTUJPTjEyUiBhbmQgTUJPTjEzUiBvbmx5IGhhdmUgYnJhbmNoZXMgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keS4KNikgTUJPTjAzIGhhcyBiaWxhdGVyYWwgYnJhbmNoZXMsIGJ1dCBtb3JlIGNvbnRyYWxhdGVyYWxseSByZWxhdGl2ZSB0byB0aGUgbmFtZS4KNykgTUJPTjIyIGhhcyBpcHNpbGF0ZXJhbCBicmFuY2hlcyBpbiB0aGUgY2FseXggYW5kIGJpbGF0ZXJhbCBicmFuY2hlcyBpbiBTSVAvU01QLgo4KSBNQk9OMzAgaGFzIGJpbGF0ZXJhbCBicmFuY2hlcywgYnV0IG1vcmUgaXBzaWxhdGVyYWwgdG8gdGhlIGNlbGwgYm9keS4KCiMjIyBQbG90IHRoZSBjb21iaW5lZCBkaXJlY3QgYW5kIGluZGlyZWN0IHBhdGh3YXlzIGZyb20gTUJPTnMgdG8gQ1hfUgpgYGB7cn0KIyBDb21iaW5lIE1CT04gdG8gQ1hfUiBkaXJlY3QgYW5kIGluZGlyZWN0IHRhYmxlcyAKU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSA8LSBTdHJvbmdNQk9OVGFyZ2V0SW5wQ2RmICU+JSBzZWxlY3QodHlwZS5mcm9tLHR5cGUudG8sd2VpZ2h0UmVsYXRpdmUpClN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvVGFibGUgPC0gYmluZF9yb3dzKFN0cm9uZ01CT04yQ1hSVGd0VGFibGUsU3Ryb25nTUJPTjJub25DWFIyQ1hSVGd0Q29tYm9UYWJsZSwgLmlkID0gTlVMTCkKCiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBjb21iaW5lZCBwYXRod2F5IHBsb3QKTUJPTnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSR0eXBlLmZyb20pKSx1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04ybm9uQ1h3Q1hSVGd0VGFibGUkdHlwZS5mcm9tKSkpKQpudW1NQk9OcyA8LSBsZW5ndGgoTUJPTnR5cGVzKQptaWROb2RlcyA8LSB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRndFIyQ1hSVGFibGUkdHlwZS5mcm9tKSkKbnVtTWlkTm9kZXMgPC0gbGVuZ3RoKG1pZE5vZGVzKQphbGxDWFJ0YXJnZXRzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFJUZ3RUYWJsZSR0eXBlLnRvKSksdW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9Obm9uQ1hUZ3RSMkNYUlRhYmxlJHR5cGUudG8pKSkpCm51bUNYdGFyZ2V0cyA8LSBsZW5ndGgoYWxsQ1hSdGFyZ2V0cykKdHlwZXMgPC0gdW5pcXVlKGMoTUJPTnR5cGVzLG1pZE5vZGVzLGFsbENYUnRhcmdldHMpKQpudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCgp4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyhyZXAoLTEsdGltZXMgPSBudW1NQk9OcyksIHJlcCgwLHRpbWVzID0gbnVtTWlkTm9kZXMpLCByZXAoMSx0aW1lcyA9IG51bUNYdGFyZ2V0cykpLCAKICAgICAgICAgICAgICAgICAgICAgIHkgPSBjKHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1NQk9OcyksIHNlcSgwLDIsbGVuZ3RoLm91dCA9IG51bU1pZE5vZGVzKSwgc2VxKC0xLDEuNSxsZW5ndGgub3V0ID0gbnVtQ1h0YXJnZXRzKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKIyBTdHJvbmdNQk9OMkNYUlRndERyY3RJbmRyY3RDb21ib1BhdGggPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvVGFibGUseHlMb29rdXAsRkFMU0UsVFJVRSkKU3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9QYXRoIDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMkNYUlRndERyY3RJbmRyY3RDb21ib1RhYmxlLHh5TG9va3VwLEZBTFNFLFRSVUUpClN0cm9uZ01CT04yQ1hSVGd0RHJjdEluZHJjdENvbWJvUGF0aCA8LSBTdHJvbmdNQk9OMkNYUlRndERyY3RJbmRyY3RDb21ib1BhdGggKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChTdHJvbmdNQk9OMkNYUlRndERyY3RJbmRyY3RDb21ib1BhdGgpCmdnc2F2ZSgiU3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9QYXRoLnN2ZyIsIHBsb3Q9U3Ryb25nTUJPTjJDWFJUZ3REcmN0SW5kcmN0Q29tYm9QYXRoLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0zMCwgaGVpZ2h0PTkwLCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCmBgYAoKIyMjIEZpbmQgY29ubmVjdGlvbnMgZnJvbSBzdHJvbmcgTUJPTiB0YXJnZXRzIHRvIHNlY29uZGFyeSB0YXJnZXRzIGluIENYCmBgYHtyfQojIEZpbmQgdGhlIHBvc3RzeW5hcHRpYyBwYXJ0bmVycyBvZiBTdHJvbmdNQk9OMkNYX1RhcmdldE5ldXJvbnMKIyBTdHJvbmdNQk9OMkNYX1RhcmdldHNDbmN0VGFibGUgPC0gZ2V0Q29ubmVjdGlvblRhYmxlKFN0cm9uZ01CT04yQ1hfVGFyZ2V0TmV1cm9ucyxzeW5hcHNlVHlwZT0iUE9TVCIpCiMgaGVhZChTdHJvbmdNQk9OMkNYX1RhcmdldHNDbmN0VGFibGUpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIDwtIGdldENvbm5lY3Rpb25UYWJsZV9mb3JTdWJzZXQoU3Ryb25nTUJPTjJDWF9UYXJnZXROZXVyb25zJGJvZHlpZCwgU3Ryb25nTUJPTjJDWF9UYXJnZXRzQ25jdFRhYmxlJHRvLCJGQiIpCgojIENvbWJpbmUgc3Ryb25nIGRpcmVjdCBhbmQgaW5kaXJlY3QgTUJPTiB0YXJnZXRzIGluIHRoZSBDWApTdHJvbmdNQk9OMkNYYWxsVGFyZ2V0c1R5cGUgPC0gdW5pcXVlKGMoU3Ryb25nTUJPTjJDWF9UYXJnZXRzVHlwZSx1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT05ub25DWFRhcmdldFIyQ1hSdGFiJGRhdGFiYXNlVHlwZS50bykpKSkKU3Ryb25nTUJPTjJDWGFsbFRhcmdldE5ldXJvbnMgPC0gZ2V0VHlwZXNUYWJsZShTdHJvbmdNQk9OMkNYYWxsVGFyZ2V0c1R5cGUpClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiA8LSBnZXRDb25uZWN0aW9uVGFibGUoU3Ryb25nTUJPTjJDWGFsbFRhcmdldE5ldXJvbnMkYm9keWlkLHN5bmFwc2VUeXBlPSJQT1NUIixzbGN0Uk9JPSJGQiIpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIDwtIGdldENvbm5lY3Rpb25UYWJsZV9mb3JTdWJzZXQoU3Ryb25nTUJPTjJDWGFsbFRhcmdldE5ldXJvbnMkYm9keWlkLCBTdHJvbmdNQk9OMkNYX1RhcmdldHNDbmN0VGFibGUkdG8sIkZCIikKCiMgQXJyYW5nZSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIgaW4gdGhlIG9yZGVyIG9mIFN0cm9uZ01CT05UYXJnZXRJbnBDJGRhdGFiYXNlVHlwZS50bwojIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20gPC0gZmFjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20sbGV2ZWxzPXVuaXF1ZShTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8pLG9yZGVyZWQ9VFJVRSkKIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIgPC0gYXJyYW5nZShTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIsdHlwZS5mcm9tKQoKIyBTb3J0IFN0cm9uZ01CT04yQ1hhbGxUYXJnZXROZXVyb25zIGJ5IHBhdGh3YXkgd2VpZ2h0CgoKIyBQbG90IGFuZCBzYXZlIHRoZSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIgYmFzZWQgb24gdGhlIGNvbm5lY3Rpb25NZWFzdXJlIG9mICJ3ZWlnaHRSZWxhdGl2ZSIKcGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQixieUdyb3VwPSJpZCIpCnByaW50KHBsb3RTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIpCmdnc2F2ZSgicGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQi5lcHMiLCBwbG90PXBsb3RTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIsIGRldmljZT0iZXBzIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTgwLCBoZWlnaHQ9MTAsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1GQUxTRSkKCiMgR2V0IHR5cGUgdG8gdHlwZSBjb25uZWN0aW9uIHRhYmxlClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20gPC0gYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQiR0eXBlLmZyb20pClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgPC0gZ2V0VHlwZVRvVHlwZVRhYmxlKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQikKIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgJT4lIGFycmFuZ2UodHlwZS5mcm9tICVpbiUgU3Ryb25nTUJPTjJDWF9UYXJnZXRzVHlwZSkKCiMgRmlsdGVyIHRoZSB0YWJsZSBieSB3ZWlnaHRSZWxhdGl2ZQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgICU+JSBmaWx0ZXIod2VpZ2h0UmVsYXRpdmUgPiAwLjAxKQojIEV4Y2x1ZGUgY29ubmVjdGlvbnMgdG8gc2VsZiB0eXBlcwpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgJT4lIGZpbHRlcih0eXBlLmZyb20gIT0gdHlwZS50bykKCiMgQXJyYW5nZSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIGluIHRoZSBvcmRlciBvZiBTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8KIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSA8LSBmYWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20sbGV2ZWxzPXVuaXF1ZShTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8pLG9yZGVyZWQ9VFJVRSkKIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIGFycmFuZ2UoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYix0eXBlLmZyb20pCgojIFBsb3QgYW5kIHNhdmUgdGhlIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgYmFzZWQgb24gdGhlIGNvbm5lY3Rpb25NZWFzdXJlIG9mICJ3ZWlnaHRSZWxhdGl2ZSIKcGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVDb25uIDwtIHBsb3RDb25uZWN0aXZpdHlNYXRyaXgoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQpwcmludChwbG90U3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZUNvbm4pCmdnc2F2ZSgiU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYmxlX3dlaWdodFJlbGF0aXZlLmVwcyIsIHBsb3Q9cGxvdFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVDb25uLCBkZXZpY2U9ImVwcyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xMSwgaGVpZ2h0PTgsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1UUlVFKQpgYGAKCiMjIyBQbG90IHRoZSBwYXRod2F5cyBmcm9tIHN0cm9uZyBNQk9OLT5DWCB0YXJnZXRzIHRvIHNlY29uZGFyeSBDWCB0YXJnZXRzCmBgYHtyfQojIFNldCB1cCB0aGUgbGF5b3V0IGZvciB0aGUgcGF0aHdheSBwbG90CnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSxzb3J0KHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLnRvKSkpICApICAgKQpudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCm51bUZyb20gPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSkKbnVtVG8gPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLnRvKSkpCgojIEFycmFuZ2UgdGhlIHByaW1hcnkgTUJPTi0+Q1ggdGFyZ2V0cyBpbiBhIGNpcmNsZQphbmdzRnJvbSA8LSBzZXEoLXBpLHBpLGxlbmd0aC5vdXQgPSBudW1Gcm9tICsgMSkKYW5nc0Zyb20gPC0gYW5nc0Zyb21bMToobGVuZ3RoKGFuZ3NGcm9tKS0xKV0KeEZyb20gPC0gMSpzaW4oYW5nc0Zyb20pCnlGcm9tIDwtIDEqY29zKGFuZ3NGcm9tKQoKIyBBcnJhbmdlIHRoZSBzZWNvbmRhcnkgTUJPTi0+Q1ggdGFyZ2V0cyBpbiBhIGJpZ2dlciBjaXJjbGUKYW5nc1RvIDwtIHNlcSgtcGkscGksbGVuZ3RoLm91dCA9IG51bVR5cGVzIC0gbnVtRnJvbSArIDEpCmFuZ3NUbyA8LSBhbmdzVG9bMToobGVuZ3RoKGFuZ3NUbyktMSldCnhUbyA8LTMqc2luKGFuZ3NUbykKeVRvIDwtMypjb3MoYW5nc1RvKQoKIyB4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyhyZXAoLTEsdGltZXMgPSBudW1Gcm9tKSxyZXAoMCx0aW1lcyA9IG51bVR5cGVzLW51bUZyb20pKSwgCiMgICAgICAgICAgICAgICAgICAgICAgeSA9IGMoc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bUZyb20pLCBzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtVHlwZXMtbnVtRnJvbSkpKQoKIyB4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyh4RnJvbSxyZXAoMyx0aW1lcyA9IG51bVR5cGVzLW51bUZyb20pKSwgCiMgICAgICAgICAgICAgICAgICAgICAgeSA9IGMoeUZyb20sIHNlcSgtOCw4LGxlbmd0aC5vdXQgPSBudW1UeXBlcy1udW1Gcm9tKSkpCgp4eUxvb2t1cCA9IGRhdGEuZnJhbWUodHlwZSA9IHR5cGVzLCB4ID0gYyh4RnJvbSx4VG8pLCB5ID0gYyh5RnJvbSx5VG8pKQoKIyBHcmFwaCB0aGUgVHlwZVRvVHlwZSBDb25uVGFibGUgdXNpbmcgdGhlIHh5TG9va3VwIGxvb2t1cFRhYmxlClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tIDwtIGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSkKIyBnZ19NQk9OMXN0VG8ybmRhcnlUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIseHlMb29rdXAsRkFMU0UsVFJVRSkKZ2dfTUJPTjFzdFRvMm5kYXJ5VGFyZ2V0VGFiIDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLHh5TG9va3VwLEZBTFNFLFRSVUUpCmdnX01CT04xc3RUbzJuZGFyeVRhcmdldFRhYiA8LSBnZ19NQk9OMXN0VG8ybmRhcnlUYXJnZXRUYWIgKyBzY2FsZV95X3JldmVyc2UoKQpwcmludChnZ19NQk9OMXN0VG8ybmRhcnlUYXJnZXRUYWIpCmdnc2F2ZSgiTUJPTjFzdFRvMm5kYXJ5VGFyZ2V0VGFiLnN2ZyIsIHBsb3Q9Z2dfTUJPTjFzdFRvMm5kYXJ5VGFyZ2V0VGFiLCBkZXZpY2U9InN2ZyIsIHBhdGg9UGxvdERpciwgc2NhbGU9MSwgCiAgICAgICB3aWR0aD0xNiwgaGVpZ2h0PTE2LCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCmBgYAoKIyMjIENsdXN0ZXIgTUItPkNYIHByaW1hcnkgdGFyZ2V0cyBieSB0aGUgY29ycmVsYXRpb24gb2YgdGhlaXIgd2VpZ2h0UmVsYXRpdmUgY29ubmVjdGl2aXR5IHRvIHNlY29uZGFyeSB0YXJnZXRzIGFuZCB0aGUgaW52ZXJzZSAoY2x1c3RlcmluZyBzZWNvbmRhcnkgdGFyZ2V0cyBiYXNlZCBvbiB0aGVpciBpbnB1dHMpCmBgYHtyfQpTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlX2NsdXN0ZXJlZCA8LSBwbG90Q29yck1hdENsdXN0ZXIoUGxvdERpcixTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLCdTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlJykKU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5RnJvbSA8LSBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlX2NsdXN0ZXJlZFtbMV1dClN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvIDwtIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVfY2x1c3RlcmVkW1syXV0Kcm0oU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZV9jbHVzdGVyZWQpOwoKIyBNYWtlIGFuZCBwbG90IGNvc2luZSBkaXN0YW5jZSBtYXRyaXgKU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZV9Db3NEaXN0IDwtIGNvc0Rpc3RDbHVzdGVyUGxvdChQbG90RGlyLFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIsIlN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGUiKQoKYGBgCgoKYGBge3J9CiMgUGxvdCB0aGUgY29ubmVjdGlvbnMgYnkgcGF0aHdheVdlaWdodCAoPSB0b3RhbE1CQ29udHJpYnV0aW9uICogd2VpZ2h0UmVsYXRpdmUpCgpgYGAKCiMjIyBGaW5kIHRlcnRpYXJ5IE1CT04gdGFyZ2V0cyBpbiB0aGUgQ1gKYGBge3J9CiMgRmluZCB0aGUgcG9zdHN5bmFwdGljIHBhcnRuZXJzIG9mIHNlY29uZGFyeSBTdHJvbmdNQk9OMkNYX1RhcmdldE5ldXJvbnMKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9UeXBlcyA8LSB1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkZGF0YWJhc2VUeXBlLnRvKSkKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9OZXVyb25zIDwtIGdldFR5cGVzVGFibGUoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9UeXBlcykKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQiA8LSBnZXRDb25uZWN0aW9uVGFibGUoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9OZXVyb25zJGJvZHlpZCxzeW5hcHNlVHlwZT0iUE9TVCIsc2xjdFJPST0iRkIiKQojIGhlYWQoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQikKIyBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCIDwtIGdldENvbm5lY3Rpb25UYWJsZV9mb3JTdWJzZXQoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzSW5GQl9OZXVyb25zJGJvZHlpZCwgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzQ25jdFRhYmxlJHRvLCJGQiIpCgojIEFycmFuZ2UgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIGluIHRoZSBvcmRlciBvZiBTdHJvbmdNQk9OVGFyZ2V0SW5wQyRkYXRhYmFzZVR5cGUudG8KIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIkdHlwZS5mcm9tIDwtIGZhY3RvcihTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkIkdHlwZS5mcm9tLGxldmVscz11bmlxdWUoU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvKSxvcmRlcmVkPVRSVUUpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCIDwtIGFycmFuZ2UoU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCLHR5cGUuZnJvbSkKCiMgUGxvdCBhbmQgc2F2ZSB0aGUgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQiBiYXNlZCBvbiB0aGUgY29ubmVjdGlvbk1lYXN1cmUgb2YgIndlaWdodFJlbGF0aXZlIgpwbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCIDwtIHBsb3RDb25uZWN0aXZpdHlNYXRyaXgoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQixieUdyb3VwPSJpZCIpCnByaW50KHBsb3RTdHJvbmdNQk9OMkNYMm5kVGFyZ2V0c1RvUG9zdEluRkIpCmdnc2F2ZSgicGxvdFN0cm9uZ01CT04yQ1gybmRUYXJnZXRzVG9Qb3N0SW5GQi5lcHMiLCBwbG90PXBsb3RTdHJvbmdNQk9OMkNYMm5kVGFyZ2V0c1RvUG9zdEluRkIsIGRldmljZT0iZXBzIiwgcGF0aD1QbG90RGlyLCBzY2FsZT0xLCAKICAgICAgIHdpZHRoPTE4MCwgaGVpZ2h0PTgwLCB1bml0cz0iaW4iLCBkcGk9MzAwLCBsaW1pdHNpemU9RkFMU0UpCgojIEdldCB0eXBlIHRvIHR5cGUgY29ubmVjdGlvbiB0YWJsZQpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBnZXRUeXBlVG9UeXBlVGFibGUoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQikKIyBTdHJvbmdNQk9OMkNYVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIgJT4lIGFycmFuZ2UodHlwZS5mcm9tICVpbiUgU3Ryb25nTUJPTjJDWF9UYXJnZXRzVHlwZSkKCiMgRmlsdGVyIHRoZSB0YWJsZSBieSB3ZWlnaHRSZWxhdGl2ZQpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiAgJT4lIGZpbHRlcih3ZWlnaHRSZWxhdGl2ZSA+IDAuMDEpCiMgRXhjbHVkZSBjb25uZWN0aW9ucyB0byBzZWxmIHR5cGVzClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiIDwtIFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiICU+JSBmaWx0ZXIodHlwZS5mcm9tICE9IHR5cGUudG8pCgojIEFycmFuZ2UgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiBpbiB0aGUgb3JkZXIgb2YgU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20gPC0gZmFjdG9yKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tLGxldmVscz11bmlxdWUoU3Ryb25nTUJPTlRhcmdldElucEMkZGF0YWJhc2VUeXBlLnRvKSxvcmRlcmVkPVRSVUUpCiMgU3Ryb25nTUJPTjJDWFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiA8LSBhcnJhbmdlKFN0cm9uZ01CT04yQ1hUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIsdHlwZS5mcm9tKQoKIyBQbG90IGFuZCBzYXZlIHRoZSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiBiYXNlZCBvbiB0aGUgY29ubmVjdGlvbk1lYXN1cmUgb2YgIndlaWdodFJlbGF0aXZlIgpwbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZSA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLGJ5R3JvdXA9InR5cGUiLGNvbm5lY3Rpb25NZWFzdXJlPSJ3ZWlnaHRSZWxhdGl2ZSIpCnByaW50KHBsb3RTdHJvbmdNQk9OMkNYMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlKQpnZ3NhdmUoIlN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFibGVfd2VpZ2h0UmVsYXRpdmUuZXBzIiwgcGxvdD1wbG90U3Ryb25nTUJPTjJDWDJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZSwgZGV2aWNlPSJlcHMiLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MjQsIGhlaWdodD0xNiwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQpgYGAKCiMjIyBQbG90IHRoZSBwYXRod2F5cyBmcm9tIHN0cm9uZyBNQk9OLT5DWCBzZWNvbmRhcnkgQ1ggdGFyZ2V0cyB0byB0ZXJ0aWFyeSBDWCB0YXJnZXRzCmBgYHtyfQojIFNldCB1cCB0aGUgbGF5b3V0IGZvciB0aGUgcGF0aHdheSBwbG90CnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSksc29ydCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiJHR5cGUudG8pKSkgICkgICApCm51bVR5cGVzIDwtIGxlbmd0aCh0eXBlcykKbnVtRnJvbSA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSkKbnVtVG8gPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIkdHlwZS50bykpKQoKIyBBcnJhbmdlIHRoZSBzZWNvbmRhcnkgTUJPTi0+Q1ggdGFyZ2V0cyBpbiBhIGNpcmNsZQphbmdzRnJvbSA8LSBzZXEoLXBpLHBpLGxlbmd0aC5vdXQgPSBudW1Gcm9tICsgMSkKYW5nc0Zyb20gPC0gYW5nc0Zyb21bMToobGVuZ3RoKGFuZ3NGcm9tKS0xKV0KeEZyb20gPC0gNCpzaW4oYW5nc0Zyb20pCnlGcm9tIDwtIDQqY29zKGFuZ3NGcm9tKQoKIyBBcnJhbmdlIHRoZSB0ZXJ0aWFyeSBNQk9OLT5DWCB0YXJnZXRzIGluIGEgYmlnZ2VyIGNpcmNsZQphbmdzVG8gPC0gc2VxKC1waSxwaSxsZW5ndGgub3V0ID0gbnVtVHlwZXMgLSBudW1Gcm9tICsgMSkKYW5nc1RvIDwtIGFuZ3NUb1sxOihsZW5ndGgoYW5nc1RvKS0xKV0KeFRvIDwtNSpzaW4oYW5nc1RvKQp5VG8gPC01KmNvcyhhbmdzVG8pCgojIHh5TG9va3VwID0gZGF0YS5mcmFtZSh0eXBlID0gdHlwZXMsIHggPSBjKHJlcCgtMSx0aW1lcyA9IG51bUZyb20pLHJlcCgwLHRpbWVzID0gbnVtVHlwZXMtbnVtRnJvbSkpLCAKIyAgICAgICAgICAgICAgICAgICAgICB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtRnJvbSksIHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1UeXBlcy1udW1Gcm9tKSkpCgojIHh5TG9va3VwID0gZGF0YS5mcmFtZSh0eXBlID0gdHlwZXMsIHggPSBjKHhGcm9tLHJlcCgzLHRpbWVzID0gbnVtVHlwZXMtbnVtRnJvbSkpLCAKIyAgICAgICAgICAgICAgICAgICAgICB5ID0gYyh5RnJvbSwgc2VxKC04LDgsbGVuZ3RoLm91dCA9IG51bVR5cGVzLW51bUZyb20pKSkKCnh5TG9va3VwID0gZGF0YS5mcmFtZSh0eXBlID0gdHlwZXMsIHggPSBjKHhGcm9tLHhUbyksIHkgPSBjKHlGcm9tLHlUbykpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgeHlMb29rdXAgbG9va3VwVGFibGUKIyBnZ19NQk9OMm5kVG8zcmRUYXJnZXRUYWIgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLHh5TG9va3VwLEZBTFNFLFRSVUUpCmdnX01CT04ybmRUbzNyZFRhcmdldFRhYiA8LSBncmFwaENvblRhYlBvbHlDaHJvbWUoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIseHlMb29rdXAsRkFMU0UsVFJVRSkKZ2dfTUJPTjJuZFRvM3JkVGFyZ2V0VGFiIDwtIGdnX01CT04ybmRUbzNyZFRhcmdldFRhYiArIHNjYWxlX3lfcmV2ZXJzZSgpCnByaW50KGdnX01CT04ybmRUbzNyZFRhcmdldFRhYikKZ2dzYXZlKCJNQk9OMm5kVG8zcmRDWFRhcmdldFRhYi5zdmciLCBwbG90PWdnX01CT04ybmRUbzNyZFRhcmdldFRhYiwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MzAsIGhlaWdodD0zMCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQpgYGAKCiMjIyBDbHVzdGVyIE1CLT5DWCBzZWNvbmRhcnkgdGFyZ2V0cyBieSB0aGUgY29ycmVsYXRpb24gb2YgdGhlaXIgd2VpZ2h0UmVsYXRpdmUgY29ubmVjdGl2aXR5IHRvIHRlcnRpYXJ5IHRhcmdldHMgYW5kIHRoZSBpbnZlcnNlIChjbHVzdGVyaW5nIHRlcnRpYXJ5IHRhcmdldHMgYmFzZWQgb24gdGhlaXIgaW5wdXRzKQpgYGB7cn0KU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVfY2x1c3RlcmVkIDwtIHBsb3RDb3JyTWF0Q2x1c3RlcihQbG90RGlyLFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiLCdTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZScpClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUbyA8LSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZV9jbHVzdGVyZWRbWzJdXQoKIyBNYWtlIGFuZCBwbG90IGNvc2luZSBkaXN0YW5jZSBtYXRyaXgKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVfQ29zRGlzdCA8LSBjb3NEaXN0Q2x1c3RlclBsb3QoUGxvdERpcixTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYiwiU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGUiKQoKYGBgCgojIyMgUGF0aHdheSBwbG90IGJhc2VkIG9uIHRoZSBjbHVzdGVyZWQgYW5kIHJlYXJyYW5nZWQgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvCmBgYHtyfQojIFNldCB1cCB0aGUgbGF5b3V0IGZvciB0aGUgcGF0aHdheSBwbG90CnR5cGVzIDwtIHVuaXF1ZShjKHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvJHR5cGUuZnJvbSkpLHVuaXF1ZShhcy52ZWN0b3IoU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvJHR5cGUudG8pKSkpCm51bVR5cGVzIDwtIGxlbmd0aCh0eXBlcykKbnVtRnJvbSA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG8kdHlwZS5mcm9tKSkpCm51bVRvIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUbyR0eXBlLnRvKSkpCnh5TG9va3VwID0gZGF0YS5mcmFtZSh0eXBlID0gdHlwZXMsIHggPSBjKHJlcCgtMSx0aW1lcyA9IG51bUZyb20pLHJlcCgwLHRpbWVzID0gbnVtVHlwZXMtbnVtRnJvbSkpLCB5ID0gYyhzZXEoLTEsMSxsZW5ndGgub3V0ID0gbnVtRnJvbSksIHNlcSgtMSwxLGxlbmd0aC5vdXQgPSBudW1UeXBlcy1udW1Gcm9tKSkpCgojIEdyYXBoIHRoZSBUeXBlVG9UeXBlIENvbm5UYWJsZSB1c2luZyB0aGUgbG9va3VwVGFibGUKU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvX2NvcHkgPC0gU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19jb3B5JHR5cGUudG8gPC0gYXMudmVjdG9yKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19jb3B5JHR5cGUudG8pCiMgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWJfaGNCeVRvX3BhdGh3YXkgPC0gZ3JhcGhDb25UYWJfb2xkKFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19jb3B5LHh5TG9va3VwLEZBTFNFLFRSVUUpClN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19wYXRod2F5IDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fY29weSx4eUxvb2t1cCxGQUxTRSxUUlVFKQojIGdnX01CT04yVGFyZ2V0VGFiIDwtIGdyYXBoQ29uVGFiUG9seUNocm9tZShTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSx4eUxvb2t1cCxGQUxTRSxUUlVFKQpTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSA8LSBTdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSArIHNjYWxlX3lfcmV2ZXJzZSgpCnByaW50KFN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2hjQnlUb19wYXRod2F5KQpnZ3NhdmUoIlN0cm9uZ01CT04yQ1hfMm5kVGFyZ2V0c1RvUG9zdEluRkJfVHlwZTJUeXBlVGFiX2NsdXN0ZXJCeVRvX3BhdGh3YXkuc3ZnIiwgcGxvdD1TdHJvbmdNQk9OMkNYXzJuZFRhcmdldHNUb1Bvc3RJbkZCX1R5cGUyVHlwZVRhYl9oY0J5VG9fcGF0aHdheSwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAogICAgICAgd2lkdGg9MzAsIGhlaWdodD02MCwgdW5pdHM9ImluIiwgZHBpPTMwMCwgbGltaXRzaXplPUZBTFNFKQoKYGBgCgojIyMjIyMjIyMjIyByYW4gd2VsbCB1cCB0byBoZXJlICMjIyMjIyMjIyMjIyMjIyMKCiMjIyBwdXQgYSBicmVhayBoZXJlCgpgYGB7cn0KIyBSdW4gdC1TTkUgb24gdGhlIHR5cGUudG8gb2YgU3Ryb25nTUJPTjJDWF8ybmRUYXJnZXRzVG9Qb3N0SW5GQl9UeXBlMlR5cGVUYWIKIyBsaWJyYXJ5KE0zQykKIyB0c25lKERhdGE0Q2x1c3QscGVycGxleD0xNSkKCmBgYAoKIyMjIENoZWNrIGluZGlyZWN0IGNvbm5lY3Rpb25zIGZyb20gTUJPTnMgdG8gdGhlIENYIHdpdGhvdXQgbGF0ZXJhbGl6YXRpb24KYGBge3J9CiMgUGxvdCBuZXVyb24gdG8gbmV1cm9uIGNvbm5lY3Rpb24gbWF0cml4IGZyb20gTUJPTnMgdG8gbm9uLUNYIG5vbi1NQk9OIHRhcmdldHMKIyBNQk9OX25vbkNYbm9uTUJPTl9Db25uVGFibGUgPC0gTUJPTl9Qb3N0Q29ubmVjdGlvbnMgJT4lIGZpbHRlcih0eXBlLnRvICVpbiUgTUJPTm5vbkNYVGFyZ2V0c0wpCiMgcGxvdE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05fbm9uQ1hub25NQk9OX0Nvbm5UYWJsZSxieUdyb3VwPSJpZCIsY29ubmVjdGlvbk1lYXN1cmU9IndlaWdodFJlbGF0aXZlIikKIyBwcmludChwbG90TUJPTl9ub25DWG5vbk1CT05fQ29ublRhYmxlKQoKIyBQbG90IHR5cGUgdG8gdHlwZSBjb25uZWN0aW9uIG1hdHJpeCBmcm9tIE1CT05zIHRvIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzCiMgTUJPTl9ub25DWG5vbk1CT05fVHlwZTJUeXBlVGFiIDwtIGdldFR5cGVUb1R5cGVUYWJsZShNQk9OX25vbkNYbm9uTUJPTl9Db25uVGFibGUpCiMgRmlsdGVyIHRoZSB0YWJsZSBieSB3ZWlnaHRSZWxhdGl2ZQojIE1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZVRhYiA8LSBNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVUYWIgICU+JSBmaWx0ZXIod2VpZ2h0UmVsYXRpdmUgPiAwLjAxKQoKIyBwbG90TUJPTl9ub25DWG5vbk1CT05fVHlwZTJUeXBlQ29ubiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQojIHByaW50KHBsb3RNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVDb25uKQoKIyBQbG90IG5ldXJvbiB0byBuZXVyb24gY29ubmVjdGlvbiBtYXRyaXggZnJvbSBNQk9OIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzIHRvIHRoZWlyIHRhcmdldHMgaW4gdGhlIENYCiMgTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfQ29ublRhYmxlIDwtIE1CT05fMm5kYXJ5UG9zdENvbm5lY3Rpb25zICU+JSBmaWx0ZXIodHlwZS50byAlaW4lIE1CT05DWDJuZGFyeVRhcmdldHNMKQojIHBsb3RNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9Db25uVGFibGUgPC0gcGxvdENvbm5lY3Rpdml0eU1hdHJpeChNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9Db25uVGFibGUsYnlHcm91cD0iaWQiLGNvbm5lY3Rpb25NZWFzdXJlPSJ3ZWlnaHRSZWxhdGl2ZSIpCiMgcHJpbnQocGxvdE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX0Nvbm5UYWJsZSkKCiMgUGxvdCB0eXBlIHRvIHR5cGUgY29ubmVjdGlvbiBtYXRyaXggZnJvbSBNQk9OIG5vbi1DWCBub24tTUJPTiB0YXJnZXRzIHRvIHRoZWlyIHRhcmdldHMgaW4gdGhlIENYCiMgTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiIDwtIGdldFR5cGVUb1R5cGVUYWJsZShNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9Db25uVGFibGUpCiMgRmlsdGVyIHRoZSB0YWJsZSBieSB3ZWlnaHRSZWxhdGl2ZQojIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiA8LSBNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIgICU+JSBmaWx0ZXIod2VpZ2h0UmVsYXRpdmUgPiAwLjAxKQojIEV4Y2x1ZGUgY29ubmVjdGlvbnMgdG8gc2VsZiB0eXBlcwojIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiA8LSBNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIgJT4lIGZpbHRlcih0eXBlLmZyb20gIT0gdHlwZS50bykKCiMgcGxvdE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiA8LSBwbG90Q29ubmVjdGl2aXR5TWF0cml4KE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYixieUdyb3VwPSJ0eXBlIixjb25uZWN0aW9uTWVhc3VyZT0id2VpZ2h0UmVsYXRpdmUiKQojIHByaW50KHBsb3RNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIpCgojIEZpbHRlciBNQk9OX25vbkNYbm9uTUJPTl9UeXBlMlR5cGVUYWIgYmFzZWQgb24gdHlwZS50byB3aXRoIGRpcmVjdCBjb25uZWN0aW9ucyBpbiB0aGUgQ1gKIyBNQk9OX25vbkNYbm9uTUJPTndDWDJuZFRfVHlwZTJUeXBlVGFiIDwtIE1CT05fbm9uQ1hub25NQk9OX1R5cGUyVHlwZVRhYiAlPiUgZmlsdGVyKHR5cGUudG8gJWluJSB1bmlxdWUoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSkpCiMgTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiIDwtIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiAlPiUgZmlsdGVyKHR5cGUuZnJvbSAlaW4lIHVuaXF1ZShNQk9OX25vbkNYbm9uTUJPTndDWDJuZFRfVHlwZTJUeXBlVGFiJHR5cGUudG8pKQoKIyBQbG90IHBhdGh3YXlzIGZyb20gTUJPTnMgdG8gbm9uLUNYIG5vbi1NQk9OIHRhcmdldHMgdG8gc2Vjb25kYXJ5IENYIHRhcmdldHMKIyBDb21iaW5lIHRhYmxlcwojIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZUNvbWJvVGFiIDwtIGJpbmRfcm93cyhNQk9OX25vbkNYbm9uTUJPTndDWDJuZFRfVHlwZTJUeXBlVGFiLE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiwgLmlkID0gTlVMTCkKCiMgU2V0IHVwIHRoZSBsYXlvdXQgZm9yIHRoZSBwYXRod2F5IHBsb3QKIyB0eXBlcyA8LSB1bmlxdWUoYyh1bmlxdWUoYXMudmVjdG9yKE1CT05fbm9uQ1hub25NQk9Od0NYMm5kVF9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSksIHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlVGFiJHR5cGUuZnJvbSkpLAojICAgICAgICAgICAgICAgICAgdW5pcXVlKGFzLnZlY3RvcihNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIkdHlwZS50bykpKSkKIyBudW1UeXBlcyA8LSBsZW5ndGgodHlwZXMpCiMgbnVtTUJPTnMgPC0gbGVuZ3RoKHVuaXF1ZShhcy52ZWN0b3IoTUJPTl9ub25DWG5vbk1CT053Q1gybmRUX1R5cGUyVHlwZVRhYiR0eXBlLmZyb20pKSkKIyBudW1NaWROb2RlcyA8LSBsZW5ndGgodW5pcXVlKGFzLnZlY3RvcihNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVUYWIkdHlwZS5mcm9tKSkpCiMgbnVtQ1h0YXJnZXRzIDwtIGxlbmd0aCh1bmlxdWUoYXMudmVjdG9yKE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZVRhYiR0eXBlLnRvKSkpCiMgeHlMb29rdXAgPSBkYXRhLmZyYW1lKHR5cGUgPSB0eXBlcywgeCA9IGMocmVwKC0xLHRpbWVzID0gbnVtTUJPTnMpLCByZXAoMCx0aW1lcyA9IG51bU1pZE5vZGVzKSwgcmVwKDEsdGltZXMgPSBudW1DWHRhcmdldHMpKSwgCiMgICAgICAgICAgICAgICAgICAgICAgeSA9IGMoc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bU1CT05zKSwgc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bU1pZE5vZGVzKSwgc2VxKC0xLDEsbGVuZ3RoLm91dCA9IG51bUNYdGFyZ2V0cykpKQoKIyBHcmFwaCB0aGUgVHlwZVRvVHlwZSBDb25uVGFibGUgdXNpbmcgdGhlIGxvb2t1cFRhYmxlCiMgTUJPTl9ub25DWG5vbk1CT05fMm5kYXJ5Q1hfVHlwZTJUeXBlQ29tYm9QYXRoIDwtIGdyYXBoQ29uVGFiX29sZChNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1RhYix4eUxvb2t1cCxGQUxTRSxUUlVFKQojIE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZUNvbWJvUGF0aCA8LSBNQk9OX25vbkNYbm9uTUJPTl8ybmRhcnlDWF9UeXBlMlR5cGVDb21ib1BhdGggKyBzY2FsZV95X3JldmVyc2UoKQojIHByaW50KE1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZUNvbWJvUGF0aCkKIyBnZ3NhdmUoIk1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZUNvbWJvUGF0aC5zdmciLCBwbG90PU1CT05fbm9uQ1hub25NQk9OXzJuZGFyeUNYX1R5cGUyVHlwZUNvbWJvUGF0aCwgZGV2aWNlPSJzdmciLCBwYXRoPVBsb3REaXIsIHNjYWxlPTEsIAojICAgICAgIHdpZHRoPTMwLCBoZWlnaHQ9NDUsIHVuaXRzPSJpbiIsIGRwaT0zMDAsIGxpbWl0c2l6ZT1GQUxTRSkKCmBgYAoKCgoKRm9jdXMgb24gdGhlIEZCIG5vdyAodGhlIG9ubHkgZXhjZXB0aW9uIGlzIExDTnAgd2hpY2ggY2FuIHByb2JhYmx5IGJlIHRyZWF0ZWQgc2VwYXJhdGVseSkgYW5kIHRyeSB0byBjb21wdXRlIGEgInBhdGh3YXkgd2VpZ2h0IiBwZXIgQ1ggdGFyZ2V0IHR5cGUuIEZvciBldmVyeSBjb25uZWN0aW9uLCBgTUJPTkluZmx1ZW5jZWAgaXMgdGhlIHByb2R1Y3Qgb2YgdGhlIHJlbGF0aXZlIHdlaWdodCB3aXRoIHRoZSB0b3RhbCBjb250cmlidXRpb24gb2YgTUJPTnMgdG8gdGhlIHByZXN5bmFwdGljIG5ldXJvbi4gYHBhdGh3YXlzV2VpZ2h0YCBpcyB0aGUgc3VtIG9mIHRoYXQgb3ZlciBhIHRhcmdldCB0eXBlIChiYXNpY2FsbHkgdGhlIHN1bSBvZiBhbGwgaXRzIGluZGlyZWN0IE1CT04gaW5mbHVlbmNlcykuIAoKKioqU2hvdWxkIGFsc28gZmluZCBwYXRod2F5cyB0aHJvdWdoIG1pZGRsZSBuZXVyb25zIG91dHNpZGUgb2YgdGhlIENYLiAKYGBge3J9Ck1CT05UYXJnZXRGQk91dCA8LSBNQk9OVGFyZ2V0QmFnTGF0JG91dHB1dHMgJT4lIGZpbHRlcihyb2k9PSJGQiIgJiB0eXBlLmZyb20gJWluJSBNQk9OVGFyZ2V0SW5wQyR0eXBlLnRvKSAlPiUgCiAgbXV0YXRlKE1CT05JbmZsdWVuY2UgPSBNQk9OVGFyZ2V0SW5wQyR0b3RhbE1CQ29udHJpYnV0aW9uW21hdGNoKHR5cGUuZnJvbSxNQk9OVGFyZ2V0SW5wQyR0eXBlLnRvKV0qd2VpZ2h0UmVsYXRpdmUpICU+JSBncm91cF9ieSh0eXBlLnRvKSAlPiUgbXV0YXRlKHBhdGh3YXlzV2VpZ2h0ID0gc3VtKE1CT05JbmZsdWVuY2UpKSAlPiUgdW5ncm91cCgpCiAgCmBgYApMb29raW5nIGF0IHRoaXMgdGFibGUgYW5kIHNvcnRpbmcgaXQgYWNjb3JkaW5nIHRvIHZhcmlvdXMgd2F5cywgdGhlIHRoaW5ncyB0aGF0IGZpcnN0IHBvcHMgb3V0IGlzIHRoYXQgaXQncyBtb3N0bHkgYSBGQiB0YW5nZW50aWFsIG5ldHdvcmsgKG1lYW5pbmcgbW9zdCBGQiB0YW5nZW50aWFsIG5ldXJvbiB0YXJnZXRzIGluIHRoZSBGQiBhcmUgb3RoZXIgRkIgdGFuZ2VudGlhbCBuZXVyb25zLCBJIGd1ZXNzIHNvbWV0aGluZyByZW1pbmlzY2VudCBvZiB0aGUgcmluZyBuZXVyb25zKS4gU28gd2UnZCBuZWVkIGEgd2F5IHRvIHdlYW4gdGhpcyByZWN1cnJlbmNlIGluIHRoZSB3YXkgd2UgY29uc2lkZXIgd2hpY2ggcGFydHMgb2YgdGhlIG5ldHdvcmsgYXJlIGluZmx1ZW5jZWQgYnkgdGhlIE1CT05zLgpgYGB7cn0KTUJPTlRhcmdldEZCT3V0U3VtbWFyeSA8LSBNQk9OVGFyZ2V0RkJPdXQgJT4lIGdyb3VwX2J5KHR5cGUudG8pICU+JSBzdW1tYXJpc2UocGF0aHdheXNXZWlnaHQgPSBwYXRod2F5c1dlaWdodFsxXSkKYGBgCgpDb25zaWRlcmluZyBGQjRSIHNlcGFyYXRlbHkKYGBge3J9Ck1CT05UYXJnZXRGQjRSIDwtIE1CT05UYXJnZXRGQk91dCAlPiUgZmlsdGVyKHR5cGUuZnJvbSA9PSAiRkI0Ul9SIikgJT4lIGdyb3VwX2J5KHR5cGUudG8pICU+JSBzdW1tYXJpc2UocGF0aHdheXNXZWlnaHQgPSBwYXRod2F5c1dlaWdodFsxXSkKYGBgCgpTdWdnZXN0aW9uOiByZWRvIHRoYXQsIGJ1dCBzdHVkeSB0aGUgcG9vbCBvZiBuZXVyb25zIGluZmx1ZW5jZWQgYnkgYSBzaW5nbGUgTUJPTiB0eXBlIGF0IGEgdGltZS4KCk90aGVyIGF4aXMgdG8gZXhwbG9yZTogRkIgdGFuZ2VudGlhbCBpbmhvbW9nZW5lb3VzIGlucHV0cyBvbnRvIGNvbHVtbmFyIG5ldXJvbnMuCgoK